personal website.
Table of contents
Fn
FnMut
and FnOnce
under closure
borrow
and borrow_mut
not working?self.x
because it is borrowedFn
FnMut
and FnOnce
under closure
Fn
is the most flexible one, it can be called multiple times without any restrictions since they don't have any changes to their environment.
#[derive(Debug)]
struct SomeStruct {
param: i32,
}
fn check() {
let s = SomeStruct { param: 1 };
let func = || {
println!("{:?}", s);
};
func() // func is recognized as `impl Fn()`
}
FnMut
means there's environment changes in the function:
#[derive(Debug)]
struct SomeStruct {
param: i32,
}
fn use_change() {
let mut s = SomeStruct { param: 1 };
let mut func = || { // for why `FmMut` variable must have `mut`, check: https://users.rust-lang.org/t/why-must-closure-variable-be-mut/53583/3
s.param = 0;
};
func() // function is recognized as `impl FnMut()` since it changes `s` in it's environment
}
it can also run multiple times, but since in rust mut
for one variable can only exist once in the same scope, this is not allowed:
// WILL NOT COMPILE
fn use_change() {
let mut s = SomeStruct { param: 1 };
let mut func1 = || {
s.param = 0;
};
let mut func2 = || {
s.param = 1; // can not borrow the same variable twice which we did here to `s.param`, but you can still change other params under `s`
};
func1();
func2();
}
FnOnce
means the function will only be executed once, this is special because you have to specify this in some circumstances:// WILL NOT COMPILE
fn use_change1() {
fn setup(s: SomeStruct) -> impl Fn() -> Box<dyn Fn()> { //here we use `Fn` not `FnOnce`
move || {
Box::new(move || {
println!("{:?}", s);
})
}
}
let func = setup(SomeStruct { param: 1 });
// you might want to just call it once, but without specifically saying it's a `FnOnce`, compiler will assume:
let b1 = func(); // box of print function
let b2 = func(); // box of print function
// `s` is passed to `b1` in the first call of `func`, there's nothing left for `b2` unless `SomeStruct` implemented `Copy` trait
}
Warning
> #[derive]
won't work
trait Test1 {}
trait Test2 {
fn t(&self) {}
}
impl Test2 for dyn Test1 {
fn t(&self) {
// ...
}
}
This is usually a good idea to directly use self
in side a closure that that lives longer than current function.
Unless it implements Copy
or Clone
, you can self.clone()
from outside and move that cloned variable inside closure.
But this is only for FnOnce
since that cloned variable will be dropped after closure returned.
If you wish to run it multiple times, then you have to specify the lifetime, for example:
// WILL NOT COMPILE
struct S {
d: usize,
}
impl S {
fn caller_(&mut self) -> Box<dyn FnMut()> {
Box::new(|| self.d += 1) // here, it will raise:
// lifetime may not live long enough returning this value requires that `'1` must outlive `'static`
}
}
But not only errors are shown, it gives us help: to declare that the trait object captures data from argument self
, you can add an explicit '_
lifetime bound: + '_
As for why is this needed here, maybe this means that it wants you to make sure that closure is actually try to borrow self
? idk for now.
But whatever, this works:
fn caller_(&mut self) -> Box<dyn FnMut() + '_> {
Box::new(|| self.d += 1)
}
As to what The Rust Reference/Lifetime elision said: If the receiver has type &Self
or &mut Self
, then the lifetime of that reference to Self is assigned to all elided output lifetime parameters.
And so what we do up there ca be translated to:
fn caller<'a>(&'a mut self) -> Box<dyn FnMut() + 'a> {
Box::new(|| self.d += 1)
}
If you're using FnOnce
then just add move
in front.
But if the closure will be called multiple times, implementing Fn / FnMut / FnOnce
for your own struct
could work, but this feature in only available under nightly
currently(see #29625).
Or you can specify a trait and add fn call(&self)
/fn call_mut(&mut self)
yourself and combine with the struct that holds all of the variable inside that scope, pass in the struct and call call/call_mut
.
borrow
and borrow_mut
not working?Simply removes use std::borrow::{Borrow, BorrowMut}
from your code.
self.x
because it is borrowedAlways stay alert dealing with references.
impl SomeStruct {
fn a(&self) -> Result<u32, &str> {
self.p1.ok_or("no p1")
}
fn operate(&mut self) -> Result<(), &str> {
let p1 = self.a()?; // IDE gives lint: first borrow here
self.p2 = p1; // error here: cannot assign to `self.p2` because it is borrowed
Ok(())
}
}
But function a
is done and &self
borrow should be dropped, why it doesn't?
This is because the lifetime compiler inferred is incorrect:
fn a<'a>(&'a self) -> Result<(), &'a str> {
self.p1.ok_or("no p1")
}
The borrow of self
is the same lifetime of returned "no p1"
, so &self
isn't dropped at the end of function a
, instead, it goes along wherever &str
returned goes.
To fix it, you have to separate lifetime between borrow of self and ref str:
impl<'b> SomeStruct {
fn a(&self) -> Result<u32, &'b str> {
self.p1.ok_or("no p1")
}
fn operate(&mut self) -> Result<(), &'b str> {
let p1 = self.a()?;
self.p2 = p1;
Ok(())
}
}
Or in this particular case, use 'static
since "str"
itself is 'static
anyway
fn a(&self) -> Result<(), &'static str> {
self.p1.ok_or("no p1")
}
https://www.reddit.com/r/learnrust/comments/ln28un/why_does_impl_require_lifetime_annotations/
https://users.rust-lang.org/t/need-help-with-mutable-and-immutable-borrow-of-self/68811/2?u=ogios
https://manishearth.github.io/blog/2015/05/17/the-problem-with-shared-mutability/
https://github.com/rust-lang/rust/issues/46774
Note
updating...