OGIOS

personal website.


Table of contents

 Rust tips

Fn 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
    }
    

impl Trait for Trait

Warning

> #[derive] won't work

trait Test1 {}
trait Test2 {
    fn t(&self) {}
}
impl Test2 for dyn Test1 {
    fn t(&self) {
        // ...
    }
}

Return a closure that uses 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)
}

Pass in a closure which uses variable from outer scope

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.

WTF?! Why borrow and borrow_mut not working?

Simply removes use std::borrow::{Borrow, BorrowMut} from your code.

cannot assign to self.x because it is borrowed

Always 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")
}

Why does impl require lifetime annotations

https://www.reddit.com/r/learnrust/comments/ln28un/why_does_impl_require_lifetime_annotations/

std::mem::take temporary remove

https://users.rust-lang.org/t/need-help-with-mutable-and-immutable-borrow-of-self/68811/2?u=ogios

why only one mutable borrow

https://manishearth.github.io/blog/2015/05/17/the-problem-with-shared-mutability/

suffer from loop with cpu hungry but still want to get the result in time?

https://github.com/rust-lang/rust/issues/46774

Note

updating...