The iterator method take_while
takes as its argument a closure.
For example:
fn main() {
let s = "hello!";
let iter = s.chars();
let s2 = iter.take_while(|x| *x != 'o').collect::<String>();
// ^^^^^^^^^^^^^
// closure
println!("{}", s2); // hell
}
playground link
This is fine for simple closures, but if I want a more complicated predicate, I don't want to write it directly in the take_while
argument. Rather, I would like to return the closure from a function.
I seem to be having trouble getting this to work. Here is my naive attempt:
fn clos(a: char) -> Box<Fn(char) -> bool> {
Box::new(move |b| a != b)
}
fn main() {
// println!("{}", clos('a')('b')); // <-- true
// ^--- Using the closure here is fine
let s = "hello!";
let mut iter = s.chars();
let s2 = iter.take_while( clos('o') ).collect::<String>();
// ^--- This causes lots of issues
println!("{}", s2);
}
playground link
However, the error that it causes has proven difficult to understand:
error[E0277]: the trait bound `for<'r> Box<std::ops::Fn(char) -> bool>: std::ops::FnMut<(&'r char,)>` is not satisfied
--> <anon>:11:23
|
11 | let s2 = iter.take_while( clos('o') ).collect::<String>();
| ^^^^^^^^^^ trait `for<'r> Box<std::ops::Fn(char) -> bool>: std::ops::FnMut<(&'r char,)>` not satisfied
error[E0277]: the trait bound `for<'r> Box<std::ops::Fn(char) -> bool>: std::ops::FnOnce<(&'r char,)>` is not satisfied
--> <anon>:11:23
|
11 | let s2 = iter.take_while( clos('o') ).collect::<String>();
| ^^^^^^^^^^ trait `for<'r> Box<std::ops::Fn(char) -> bool>: std::ops::FnOnce<(&'r char,)>` not satisfied
|
= help: the following implementations were found:
= help: <Box<std::boxed::FnBox<A, Output=R> + 'a> as std::ops::FnOnce<A>>
= help: <Box<std::boxed::FnBox<A, Output=R> + Send + 'a> as std::ops::FnOnce<A>>
error: no method named `collect` found for type `std::iter::TakeWhile<std::str::Chars<'_>, Box<std::ops::Fn(char) -> bool>>` in the current scope
--> <anon>:11:47
|
11 | let s2 = iter.take_while( clos('o') ).collect::<String>();
| ^^^^^^^
|
= note: the method `collect` exists but the following trait bounds were not satisfied: `Box<std::ops::Fn(char) -> bool> : std::ops::FnMut<(&char,)>`, `std::iter::TakeWhile<std::str::Chars<'_>, Box<std::ops::Fn(char) -> bool>> : std::iter::Iterator`
error: aborting due to 3 previous errors
I have tried some other things, including using FnBox
, but it didn't work. I haven't used closures that much, so I'd really like to understand what is going wrong, as well as how to fix it.
Related
There are two issues in your code.
First, take_while
passes the value by reference to the function (notice the &
in where P: FnMut(&Self::Item) -> bool
), whereas your closure expects to receive it by value.
fn clos(a: char) -> Box<Fn(&char) -> bool> {
Box::new(move |&b| a != b)
}
Then there's the issue that Box<Fn(&char) -> bool>
does not implement FnMut(&char) -> bool
. If we look at the documentation for FnMut
, we'll see that the standard library provides these implementations:
impl<'a, A, F> FnMut<A> for &'a F where F: Fn<A> + ?Sized
impl<'a, A, F> FnMut<A> for &'a mut F where F: FnMut<A> + ?Sized
OK, so FnMut
is implemented for references to implementations of Fn
. We have an Fn
trait object in our hands, and it implements Fn
, so that's fine. We just need to turn the Box<Fn>
into a &Fn
. We first need to dereference the box, which produces an lvalue, then take a reference to this lvalue to produce a &Fn
.
fn main() {
let s = "hello!";
let iter = s.chars();
let s2 = iter.take_while(&*clos('o')).collect::<String>();
println!("{}", s2);
}
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