Summary
In Kotlin we can do something like that:
fun transform(toTransform: Int, transformer: (Int) -> String): String {
return transformer(toTransform)
}
And then call it:
transform(10) { int -> int.toString() } // We can name first parameter
transform(10) { it.toString() } // Or use as it
What if add this feature of Kotlin to Rust? The syntax example:
fn transform<F>(to_transform: u32, transoform: F) -> String
where F: FnOnce(u32) -> String {
transform(to_transform)
}
transform(10) { |int| int.parse().unwrap() };
transform(10) { it.parse().unwrap() }
There is we call function with two arguments: number to transform and closure, but don't parenthesize the last one.
Also in Kotlin we can call closure with receiver(it's self in Rust) and than access it as "this":
fun transformAsReceiver(toTransform: Int, transformer: Int.() -> String): String {
return transformer(toTransform)
}
transformAsReceiver(10) { this.toString() }
In Rust it'll be like:
fn transformAsReceiver<F>(to_transform: u32, transformer: F) -> String
where F: FnOnce(u32 as self) -> String {
transformer(to_transform)
}
transformAsReceiver(10) { self.parse().unwrap() }
Motivation
More readable DSL
This syntax sugar will be useful for builder pattern to create easy to read DSL without any macros e.g. Kotlin's Ktor web framework uses it for HTML and routing:
fun Application.module() {
routing {
get("/") {
val name = "Ktor"
call.respondHtml(HttpStatusCode.OK) {
head {
title {
+name
}
}
body {
h1 {
+"Hello from $name!"
}
}
}
}
}
}
There is example how GUI definition with egui will be look like:
let options = eframe::NativeOptions {
initial_window_size: Some(egui::vec2(320.0, 240.0)),
..Default::default()
};
// Our application state:
let mut name = "Arthur".to_owned();
let mut age = 42;
eframe::run_simple_native("My egui app") { move |context, _frame|
egui::CentralPanel::show(context) {
self.heading("My egui application");
self.horizontal() {
let name_label = ui.label("Your name: ");
self.text_edit_singleline(&mut name)
.labelled_by(name_label.id);
};
self.add(egui::Slider::new(&mut age, 0..=120).text("age"));
if self.button("Click each year").clicked() {
age += 1;
}
self.label(format!("Hello '{name}', age {age}"));
};
};
This code looks better than in the example.
Better than macros
Possible you ask: "But we've procmacros! Why we cannot just use them instead?".
There are two reasons:
- Autocomplete. Unfortunately rust-analyzer cannot autocomplete you in procmacros as good as in regular code.
- Readability. Code in macros has worse readability than a regular code and in some macros we haven't a Rust-way syntax.