Earlier quoted context omitted.
This is correct. But it’s not really surprising, it’s type inference.
It isn't really type inference. Each closure gets a unique type. Rather it's an automatic decision of what traits (think roughly "superclasses" I guess if you aren't familiar with traits/typeclasses) to implement for that type.
Understanding Rust Closures
21–30 of 37 posts
Re: Understanding Rust Closures
#22if I'm not mistaken (and I very well may be!) my primary confusion with closures comes from the fact that: the trait they implement (FnOnce / Fn / FnMut) depends entirely upon what happens inside the closure. It will automatically implement the most general, relaxed version (FnMut I think?) and only restrict itself further to FnOnce and Fn based on what you do inside the closure. So, it can be tricky to know what's g…
Re: Understanding Rust Closures
#23I wish there was more customizability with regards to captures. the move keywords captures everything. Sometimes I want a little bit more flexibility, like C++ lambdas.
I agree it would be nice, in particular to make it easier to understand when learning the concept.
Re: Understanding Rust Closures
#24Earlier quoted context omitted.
It isn't really type inference. Each closure gets a unique type. Rather it's an automatic decision of what traits (think roughly "superclasses" I guess if you aren't familiar with traits/typeclasses) to implement for that type.
So you're saying... it's type inference of type classes, just like in Haskell?
Re: Understanding Rust Closures
#25Closures are the bread and butter of functional programming, but Rust made closures a complicated mess.
Closures are a complicated mess. Functional programming languages hide the mess with garbage collection.
Re: Understanding Rust Closures
#26So then I'm forced to define a trait for the function, define a struct (the closure) to store the references I want to close over, choose the mutability and lifetimes, instantiate it manually and pass that. Then the implementation of the method (that may only be a few lines) is not located inline so readability may suffer.
Re: Understanding Rust Closures
#27The biggest friction I experience with respect to rust closures is their inability to be generic: I cannot implement a method that takes a closure generic over its argument(s). So then I'm forced to define a trait for the function, define a struct (the closure) to store the references I want to close over, choose the mutability and lifetimes, instantiate it manually and pass that. Then the implementation of the metho…
Re: Understanding Rust Closures
#28Earlier quoted context omitted.
> I always forget the order of precedence for FnOnce/Fn/FnMut The way I remember the ordering is by thinking about the restrictions the various Fn traits provide from a caller's perspective: 1. FnOnce can only ever be called once and cannot be called concurrently. This is the most restrictive. 2. FnMut can be called multiple times but cannot be called concurrently. This is less restrictive than FnOnce. 3. Fn can be c…
Fn can only be called concurrently if its environment is Sync, which is often true but not necessarily. It’s more precise to say that Fn can be called even when you only have shared access to it, which is a necessary, but not sufficient, condition for being able to be called concurrently.
Re: Understanding Rust Closures
#29The biggest friction I experience with respect to rust closures is their inability to be generic: I cannot implement a method that takes a closure generic over its argument(s). So then I'm forced to define a trait for the function, define a struct (the closure) to store the references I want to close over, choose the mutability and lifetimes, instantiate it manually and pass that. Then the implementation of the metho…
fn foo(f: F)
where
F: Fn(T),
T: ToString,
{
f("Hello World")
}
Or did I not understand what you meant?Re: Understanding Rust Closures
#30The biggest friction I experience with respect to rust closures is their inability to be generic: I cannot implement a method that takes a closure generic over its argument(s). So then I'm forced to define a trait for the function, define a struct (the closure) to store the references I want to close over, choose the mutability and lifetimes, instantiate it manually and pass that. Then the implementation of the metho…
You most definitely can. fn foo (f: F) where F: Fn(T), T: ToString, { f("Hello World") } Or did I not understand what you meant?
fn foo(f: impl for Fn(T)) {
f(“Hello World”);
f(3.14);
}
fn main() {
f(|x| println!("{}", x.to_string());
}
The workaround: trait FnToString {
fn call(&self, x: impl ToString);
}
fn foo(f: impl FnToString) {
f.call("Hello World");
f.call(3.14);
}
struct AnFnToString;
impl FnToString for AnFnToString {
fn call(&self, x: impl ToString) {
println!("{}", x.to_string());
}
}
fn main() {
foo(AnFnToString);
}