fn main() {
let s = Some("xyz".to_string()); //compiler error
let foo = Box::new(|| s) as Box<Fn() -> Option<String>>; //ok
let bar = Box::new(|| Some("xyz".to_string())) as Box<Fn() -> Option<String>>;
println!("{:?}", foo());
println!("{:?}", bar());
}
gives the error
error[E0277]: the trait bound `[closure@src/main.rs:5:24: 5:28 s:std::option::Option<std::string::String>]: std::ops::Fn<()>` is not satisfied
--> src/main.rs:5:15
|
5 | let foo = Box::new(|| s) as Box<Fn() -> Option<String>>;
| ^^^^^^^^^^^^^^ the trait `std::ops::Fn<()>` is not implemented for `[closure@src/main.rs:5:24: 5:28 s:std::option::Option<std::string::String>]`
|
= note: required for the cast to the object type `std::ops::Fn() -> std::option::Option<std::string::String>`
error: aborting due to previous error
The docs for Trait std::ops::Fn state:
Fn is implemented automatically by closures which only take immutable references to captured variables or don't capture anything at all,
s isn't mutable, but it's not a reference and I'm moving it.
If I call s.clone() the compiler error goes away, but in my real case I want to avoid that.
Same error if I use FnMut FnOnce complains about not knowing the size, even though it is Boxed.
Historical note:
FnOnceused to give an error, but it works starting with Rust 1.35.
Is there a way I can make this work with moved data?
playground
If this was allowed, what would happen the second time the closure was called? Remember, the first time the closure is called, it moves s, so s now has no valid value.
There are a few ways to make this work, depending on what you need exactly.
Make the closure return a reference to the string instead.
Note: We need to write out 'a explicitly on the right-hand side of the as cast expression, otherwise the compiler gives an error. I don't think we can write a correct lifetime without introducing an intermediate function (make_foo here).
fn make_foo<'a>(s: &'a Option<String>) -> Box<dyn Fn() -> Option<&'a str> + 'a> {
Box::new(move || s.as_ref().map(|s| &**s)) as Box<dyn Fn() -> Option<&'a str> + 'a>
}
fn main() {
let s = Some("xyz".to_string());
let foo = make_foo(&s);
println!("{:?}", foo());
}
Use FnOnce instead of Fn. FnOnce closures can move, but can be called at most once.
fn main() {
let s = Some("xyz".to_string());
let foo = Box::new(|| s) as Box<dyn FnOnce() -> Option<String>>;
println!("{:?}", foo());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With