So what motivates this post is partly this reddit post, and partly the error you get when trying to do the following:
fn use_closure<F: Fn(&i32) -> &i32>(f: F) {
let x = 42;
f(&x);
}
fn main() {
let closure = |x| x;
use_closure(closure);
}
I am wondering how feasible it is to actually properly desugar closures into struct
s, so that the reasoning around them is clear. I think there are still a few bugs currently around lifetimes with closures (this one included) and most of these errors come from the fact that (AFAIK) it’s not a “true” desugaring.
Here’s some desugaring examples to see the kind of thing I’m proposing:
|x| x
desugars to…
struct Id;
impl<T> FnOnce<(T,)> for Id {
type Output = T;
extern "rust-call" fn call_once(self, (t,): (T,)) -> T {
t
}
}
impl<T> FnMut<(T,)> for Id {
extern "rust-call" fn call_mut(&mut self, (t,): (T,)) -> T {
t
}
}
impl<T> Fn<(T,)> for Id {
extern "rust-call" fn call(&self, (t,): (T,)) -> T {
t
}
}
Id
let x: i32;
|y| x + y
desugars to…
struct Add {
x: i32,
}
impl<T> FnOnce<(T,)> for Add
where i32: std::ops::Add<T>
{
type Output = <i32 as std::ops::Add<T>>::Output;
extern "rust-call" fn call_once(self, (y,): (T,)) -> Self::Output {
self.x + y
}
}
impl<T> FnMut<(T,)> for Add
where i32: std::ops::Add<T>
{
extern "rust-call" fn call_mut(&mut self, (y,): (T,)) -> Self::Output {
self.x + y
}
}
impl<T> Fn<(T,)> for Add
where i32: std::ops::Add<T>
{
extern "rust-call" fn call(&self, (y,): (T,)) -> Self::Output {
self.x + y
}
}
Add {
x: x
}
|f, x| f(x, t)
desugars to…
struct Bind<T> {
t: T,
}
impl<F, T, X> FnOnce<(F, X)> for Bind<T>
where F: FnOnce<(X, T)>
{
type Output = <F as FnOnce<(X, T)>>::Output;
extern "rust-call" fn call_once(self, (f, x): (F, X)) -> Self::Output {
f(x, self.t)
}
}
impl<F, T, X> FnMut<(F, X)> for Bind<T>
where F: FnOnce<(X, T)>, T: Copy
{
extern "rust-call" fn call_mut(&mut self, (f, x): (F, X)) -> Self::Output {
f(x, self.t)
}
}
impl<F, T, X> Fn<(F, X)> for Bind<T>
where F: FnOnce<(X, T)>, T: Copy
{
extern "rust-call" fn call(&self, (f, x): (F, X)) -> Self::Output {
f(x, self.t)
}
}
Bind {
t: t,
}
Another idea I had is that, once a closure has been desugared into an actual struct
, it can be named. That is, if I want a struct to have a particular name instead of a compiler-generated name, I can specify that explicitly, so that I could do the following:
fn generate_id() -> Id {
'Id |x| x
}
fn generate_add_one() -> AddOne {
'AddOne |x| x + 1
}
fn concatenate(mut base: String) -> Concat {
'Concat move |next| {
base += &format!("{}", next);
base.clone()
}
}
fn main() {
let mut concat = concatenate("Things: ".to_string());
concat(42);
concat(" is a number, ");
concat('a');
println!("{}", concat(" is a char.")); // prints "Things: 42 is a number, a is a char."
}
Going crazier, once a closure has been named, it can have Clone
, Copy
, Debug
and friends automatically derive
d for it. So you could do the following:
fn set_new_value<T: Debug>(x: Arc<Mutex<T>>) -> ValueSetter<T> {
#[derive(Clone)]
'ValueSetter move |value| {
let mut x = x.lock().unwrap();
println!("Old value: {:?}", *x);
*x = value;
println!("New value: {:?}", *x);
}
}
fn main() {
let x = Arc::new(Mutex::new(0));
let set = set_new_value(x);
let set_2 = set.clone();
thread::spawn(move || loop { set(42) });
loop { set_2(0) }
}
You could even (I suppose) elide the #[derive(Clone)]
and have it automagically derived where possible.
Anyway just some general ramblings / unrelated ideas about closures - just wanted to get some feedback from you guys if there is anything sensible coming out of my wobbly brain ^^