I am storing a closure in a struct like this:
#[derive(Clone)]
struct S<'a> {
func: &'a FnOnce() -> u32
}
fn main() {
let s = S { func: &|| 0 };
let val = (s.func)();
println!("{}", val);
}
When I compile, s.func cannot be moved to execute itself. I understand why it cannot be moved (namely that it's only a reference and that its size is not known at compile time), but don't know why it's being moved at all -- is it just because closures are implemented via traits?
Here's the error message:
error[E0161]: cannot move a value of type std::ops::FnOnce() -> u32:
the size of std::ops::FnOnce() -> u32 cannot be statically determined
--> main.rs:8:15
|
8 | let val = (s.func)();
| ^^^^^^^^
error[E0507]: cannot move out of borrowed content
--> main.rs:8:15
|
8 | let val = (s.func)();
| ^^^^^^^^ cannot move out of borrowed content
error: aborting due to 2 previous errors
Is this only way the solve this to store the closure on the heap (via Box<FnOnce() -> u32>)? And why does calling a closure move it? Presumably calling it doesn't mutate the function itself.
The closure is being moved because FnOnce::call_once takes self by value. This contract enforces the guarantee that the function will not be called more than once.
If you will indeed be calling the closure at most once, and you want to use the FnOnce trait, then your struct needs to take ownership of that closure (and you will need to make your struct generic on the closure type). Note that calling the closure will move the closure out of your struct, thereby invalidating the whole struct; you may work around that by wrapping the FnOnce in an Option and take-ing the closure out of the Option before calling it.
If you might be calling the closure more than once, you don't want to take ownership of the closure, or you don't want to make your struct generic on the closure type, then you should use either Fn or FnMut instead. Fn::call takes self by reference and FnMut::call_mut takes self by mutable reference. Since both accept references, you can use trait objects with them.
As explained by Francis, declaring a closure FnOnce tells Rust that you accept the broadest class of closures, including those that exhaust the objects they capture. That such closures are invoked only once is ensured by the compiler by destroying the closure object itself (by moving it into its own call method) when invoked.
It is possible to use FnOnce and still not have S generic on the closure, but it requires some work to set things up so that the closure can't be possibly invoked more than once:
the closure must be stored in an Option, so its contents can be "stolen" and the Option replaced with None (this part ensures that the closure won't be called twice);
invent a trait that knows how to steal the closure from the option and invoke it (or do something else if the closure was already stolen);
store a reference to the trait object in S - this enables the same S type works on different closures without being generic on closure type.
The result looks like this:
trait Callable {
fn call_once_safe(&mut self, default: u32) -> u32;
}
impl<F: FnOnce() -> u32> Callable for Option<F> {
fn call_once_safe(&mut self, default: u32) -> u32 {
if let Some(func) = self.take() {
func()
} else {
default
}
}
}
struct S<'a> {
func: &'a mut Callable
}
impl<'a> S<'a> {
pub fn invoke(&mut self) -> u32 {
self.func.call_once_safe(1)
}
}
fn main() {
let mut s = S { func: &mut Some(|| 0) };
let val1 = s.invoke();
let val2 = s.invoke();
println!("{} {}", val1, val2);
}
The only place that knows details about the closure is the implementation of Callable for the particular Option<F>, generated for each closure and referenced by the vtable of the &mut Callable fat pointer created when initializing the func in S.
Related
I think there is a subtle issue with what I'm trying to do here but I can't quite figure out why. I am attempting to clone a Box type (I know the type inside) but I think the lifetime is being propagated out somehow.
struct Thing
{
internal:Box<dyn std::any::Any>
}
impl Thing
{
fn new<T:'static>(u:usize) -> Self
{
return Self
{
internal:Box::new(u)
}
}
fn clone_func<T:'static>(&self) -> Self
{
return Self
{
internal:Box::new(self.internal.downcast_ref::<T>().unwrap().clone())
}
}
}
pub fn main()
{
let a = Thing::new::<usize>(12usize);
let b = a.clone_func::<usize>();
}
Error
error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
--> <source>:19:45
|
15 | fn clone_func<T:'static>(&self) -> Self
| ----- this data with an anonymous lifetime `'_`...
...
19 | internal:Box::new(self.internal.downcast_ref::<T>().unwrap().clone())
| -----------------------^^^^^^^^^^^^------------------------- ...is captured and required to live as long as `'static` here
error: aborting due to previous error
The problem is that you didn't request T to be Clone, so when you called self.internal.downcast_ref::<T>().unwrap().clone(), you actually cloned the reference, and tried to box it1.
Boxing &T works as far as the types are concerned (because it's a Box<dyn Any>), but it fails borrow checking. While T is guaranteed not to contain references to non-static data, the &T refers to the data inside &self which is not 'static, but has the anonymous lifetime of &self. This is actually pointed out by the compiler, but the error doesn't make sense without context.
Simply changing the trait bound to T: Clone + 'static makes the example compile:
fn clone_func<T: Clone + 'static>(&self) -> Self {
return Self {
internal: Box::new(self.internal.downcast_ref::<T>().unwrap().clone()),
};
}
Playground
1
A shared reference is Clone and Copy because once you have one, you're allowed to create more references to the same data. Normally, given a foo: &T and a T: Clone, foo.clone() will resolve to T::clone(foo), which returns a T as expected. But if T isn't Clone, foo.clone() resolves to <&T>::clone(&foo), which returns another &T referring to the same T value as foo, i.e. it "clones" foo itself rather than the intended *foo.
For example, in this snippet, the type of b.clone() is &X, not X (playground):
//#[derive(Clone)]
struct X;
fn main() {
let a = X;
let b = &a;
let () = b.clone(); // note type of b.clone() is &X, not X
}
If you uncomment #[derive(Clone)], the type of b.clone() becomes the expected X.
How do you pass a closure to an object-safe trait method or otherwise via dynamic dispatch?
I have answered this myself, but the answer leaves something wanting: FnOnce closures must be boxed since they are not sized and must be consumed on use (thus cannot be passed by reference).
You can pass it without using Box:
fn pass_fn_once(do_thing: &dyn FnOnce()) {
//do_thing();
}
fn main() {
pass_fn_once(&|| println!("Hello!"));
}
However, you won't be able to actually call do_thing in pass_fn_once, since it is only borrowed - calling an FnOnce consumes it.
If they are Boxed, you can be sure that pass_fn_once took ownership so that the closure can be thrown away afterwards.
Fn and FnMut closures can be passed by reference, as follows. FnOnce can be passed by reference but not called unless it is owned, e.g. by a Box.
fn pass_fn(get_num: &dyn Fn() -> i32) {
let _num = get_num();
}
fn pass_fn_mut(set_num: &mut dyn FnMut(i32)) {
set_num(6);
}
fn pass_fn_once(do_thing: Box<dyn FnOnce()>) {
do_thing();
}
fn main() {
pass_fn(&|| 2);
let mut x = 1;
pass_fn_mut(&mut |y| x = y);
pass_fn_once(Box::new(|| println!("Hello!")));
}
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.
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<Fn() -> Option<&'a str> + 'a> {
Box::new(move || s.as_ref().map(|s| &**s)) as Box<Fn() -> Option<&'a str> + 'a>
}
fn main() {
let s = Some("xyz".to_string());
let foo = make_foo(&s);
println!("{:?}", foo());
}
Use FnOnce FnBox instead of Fn. FnOnce closures can move, but can be called at most once. Since we can't call a Box<FnOnce()> as of Rust 1.23.0, we need to use Box<FnBox()>.
#![feature(fnbox)]
use std::boxed::FnBox;
fn main() {
let s = Some("xyz".to_string());
let foo = Box::new(|| s) as Box<FnBox() -> Option<String>>;
println!("{:?}", foo());
}
However, since FnBox is unstable, you can only use it with a nightly compiler. If you want to support stable compilers, you can use the BoxFnOnce type defined in the boxfnonce crate instead (though you need to explicitly call the closure as x.call(); x() won't work).
I am trying to write a small wrapper around VecDeque.
Specifically I have the code (playground):
use std::collections::VecDeque;
trait VecCircleTraits<T: Eq> {
fn new() -> VecCircle<T>;
fn find_and_remove(&self, _: T) -> Option<T>;
}
#[derive(Debug)]
struct VecCircle<T: Eq>(VecDeque<T>);
impl<T: Eq> VecCircleTraits<T> for VecCircle<T> {
fn new() -> VecCircle<T> {
return VecCircle(VecDeque::<T>::new());
}
fn find_and_remove(&self, key: T) -> Option<T> {
let search_index: Option<usize> = self.0.into_iter().position(|x| x == key); //error 1
if let Some(index) = search_index {
return self.0.remove(index); // error 2
} else {
return None;
}
}
}
Which gives me the following errors:
error: cannot borrow immutable anonymous field `self.0` as mutable
--> <anon>:20:20
|>
20 |> return self.0.remove(index); // error 2
|> ^^^^^^
error: cannot move out of borrowed content [--explain E0507]
--> <anon>:18:44
|>
18 |> let search_index: Option<usize> = self.0.into_iter().position(|x| x == key); //error 1
|> ^^^^ cannot move out of borrowed content
However, I am little confused as who has ownership over self.0? If I am understanding the docs correctly, wouldn't the memory region be bounded to self.0 and therefore giving it the ownership? Sorry for the shallow logic there but I am still trying to understand the ownership system.
In find_and_remove, you specified &self in the parameter list. This means that the method will receive a borrowed pointer to self; i.e. the type of self is &VecCircle<T>. Therefore, the method doesn't have ownership of the VecCircle<T>.
find_and_remove tries to call into_iter on a VecDeque, and into_iter receives its argument by value (self rather than &self or &mut self). Because of this, Rust interprets self.0 as trying to move the VecDeque out of the VecCircle. However, that's not allowed as you can't move anything out of borrowed content, as moving from some location makes that location invalid. But we can't just tell the caller "Hey, I just invalidated self, stop using it!"; if we wanted to do that, we'd have to specify self in the parameter list, rather than &self.
But that's not what you're trying to do here. into_iter would take ownership of the VecDeque and therefore destroy it. There are other ways to obtain an iterator for the VecDeque without destroying it. Here, we should use iter, which takes &self.
Then, find_and_remove tries to call remove. remove takes &mut self, i.e. a mutable reference to a VecDeque. However, we can't borrow self.0 as mutable, because self is not itself a mutable borrow. We can't just upgrade an immutable borrow to a mutable borrow: it is invalid to have both an immutable borrow and a mutable borrow usable at the same time. The solution here is to change &self to &mut self in the parameter list.
use std::collections::VecDeque;
trait VecCircleTraits<T: Eq> {
fn new() -> VecCircle<T>;
fn find_and_remove(&mut self, _: &T) -> Option<T>;
}
#[derive(Debug)]
struct VecCircle<T: Eq>(VecDeque<T>);
impl<T: Eq> VecCircleTraits<T> for VecCircle<T> {
fn new() -> VecCircle<T> {
return VecCircle(VecDeque::<T>::new());
}
fn find_and_remove(&mut self, key: &T) -> Option<T> {
let search_index: Option<usize> = self.0.iter().position(|x| x == key);
if let Some(index) = search_index {
self.0.remove(index)
} else {
None
}
}
}
Note: I also changed the key parameter to &T to solve another error, this time in the closure passed to position. Since iter iterates over references to the items in the VecDeque, position passes references to the closure. Since find_and_remove doesn't actually need to take ownership of the key, it should just receive an immutable borrow to it, so that both x and key are of type &T and thus we can apply == to them.
No, you don't have ownership of the VecCircle inside the find_and_remove method. All you need to know is in the function definition:
impl<T: Eq> VecCircleTraits<T> for VecCircle<T> {
fn find_and_remove(&self, key: T) -> Option<T>
}
This means that you are borrowing a reference to VecCircle. The longer way to write this would be
fn find_and_remove(self: &VecCircle, key: T) -> Option<T>
Perhaps that is more evident?
Since you don't have ownership of self, you cannot have ownership of self.0.
Can anyone tell what the problem is with the following code? The compiler is complaining about lifetimes, but the error message makes absolutely no sense. I've tried everything I could think of, but nothing seems to help.
use std::borrow::BorrowMut;
trait Trait<'a> {
fn accept(&mut self, &'a u8);
}
struct Impl<'a>{
myref: Option<&'a u8>,
}
impl<'a> Trait<'a> for Impl<'a> {
fn accept(&mut self, inp: &'a u8) { self.myref = Some(inp); }
}
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl{myref: None})
}
fn user<'a>(obj: &mut Trait<'a>) {}
fn parent<'a>(x: &'a u8) {
let mut pool = new();
user(pool.borrow_mut());
}
The compiler error is
error: `pool` does not live long enough
--> src/wtf.rs:22:10
|
22 | user(pool.borrow_mut());
| ^^^^ does not live long enough
23 | }
| - borrowed value dropped before borrower
|
= note: values in a scope are dropped in the opposite order they are created
Which makes absolutely no sense. How is the borrower outliving anything? I'm not even using the borrowed value!
Ok, this does make sense, but it's hard to see due to lifetime elision. So, here's your code with all the lifetimes written out explicitly, and with irrelevant details culled:
use std::borrow::BorrowMut;
trait Trait<'a> {}
struct Impl<'a> {
myref: Option<&'a u8>,
}
impl<'a> Trait<'a> for Impl<'a> {}
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl { myref: None })
}
fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'b)) {}
fn parent() {
/* 'i: */ let mut pool/*: Box<Trait<'x> + 'x>*/ = new();
/* 'j: */ let pool_ref/*: &'i mut Box<Trait<'x> + 'x>*/ = &mut pool;
/* BorrowMut<T>::borrow_mut<'d>(&'d mut Self) -> &'d mut T */
/* 'k: */ let pool_borrow/*: &'i mut (Trait<'x> + 'x)*/ = Box::borrow_mut(pool_ref);
user(pool_borrow);
}
Now, from the perspective of the last line of parent, we can work out the following equivalences by just reading the definition of user and substituting the lifetimes we have in parent:
'a = 'x
'b = 'i
'b = 'x
Furthermore, this lets us conclude that:
'x = 'i
This is the problem. Because of the way you've defined user, you've put yourself in a situation where the lifetime of the pool_ref borrow (which is equal to the lifetime of the pool storage location you're borrowing from) must be the same as the lifetime 'x being used in the thing being stored in pool.
It's a bit like the Box being able to have a pointer to itself before it exists, which doesn't make any sense.
Either way, the fix is simple. Change user to actually have the correct type:
fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'a)) {}
This matches the type produced by new. Alternately, just don't use borrow_mut:
user(&mut *pool)
This works because it is "re-borrowing". Calling borrow_mut translates the lifetimes more or less directly, but re-borrowing allows the compiler to narrow the borrows to shorter lifetimes. To put it another way, explicitly calling borrow_mut doesn't allow the compiler enough freedom to "fudge" the lifetimes to make them all line up, re-borrowing does.
As a quick aside:
I'm not even using the borrowed value!
Irrelevant. Rust does type- and lifetime-checking entirely locally. It never looks at the body of another function to see what it's doing; it goes on the interface alone. The compiler neither checks, nor cares, what you're doing inside a different function.
Note that there's more to the error message:
error: `pool` does not live long enough
--> src/main.rs:25:10
|>
25 |> user(pool.borrow_mut());
|> ^^^^
note: reference must be valid for the block at 23:25...
--> src/main.rs:23:26
|>
23 |> fn parent<'a>(x: &'a u8) {
|> ^
note: ...but borrowed value is only valid for the block suffix following statement 0 at 24:25
--> src/main.rs:24:26
|>
24 |> let mut pool = new();
|> ^
Let's look at user:
fn user<'a>(obj: &mut Trait<'a>) {}
This says that it will accept a mutable reference (with an unnamed lifetime) to a trait object parameterized with the lifetime 'a.
Turning to new, I'd say the method is highly suspicious:
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl { myref: None })
}
This says that it will return a boxed trait object with whatever lifetime the caller specifies. That basically never makes sense.
All that said, I'm not clear why the code chooses to use borrow_mut. I would have written that more directly:
user(&mut *pool);
This dereferences the Box<Trait> to get a Trait, then takes a mutable reference, yielding &mut Trait, which compiles.
I cannot currently explain why BorrowMut differs in behavior.
I'm not sure why this error happens, but I can give solutions!
First, it seems that using borrow_mut unnecessarily restricts the lifetime of the returned reference. Using operators to create the reference solves the error.
fn parent() {
let mut pool = new();
user(&mut *pool);
}
However, if we don't do that, we can solve the error by adding a lifetime bound to the Trait object in user's obj argument.
fn user<'a>(obj: &mut (Trait<'a> + 'a)) {}