Rustコトハジメ

プログラミング言語Rustと競プロに関する情報をお届けします。

Closure in Rust is generated by the compiler

The three types of closure in Rust

In Rust, compiler generates distinct class for each closure in code and they implements Fn or FnMut or FnOnce whereFn: FnMut and FnMut: FnOnce. In venn diagram it would be like this:

f:id:akiradeveloper529:20190307141032j:plain

pub trait Fn<Args> : FnMut<Args> {
    extern "rust-call" fn call(&self, args: Args) -> Self::Output;

pub trait FnMut<Args> : FnOnce<Args> {
    extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;

pub trait FnOnce<Args> {
    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;

They have property as follows:

  • Fn: No side effect to the captured environment. Shareable.
  • FnMut: Does side effect to the environment. Non-shareable.
  • FnOnce: Drop captured environment. Called only once. Non-shareable.

Mix immutable borrow and mutable borrow in closure

What if there mixes immutable borrow and mutable borrow in a closure?

fn main() {
    let mut v1 = vec![1,2];
    let v2 = vec![3];
    let mut f = || -> () {
        v1.push(v2[0]);
    };
    println!("{:?}", v2);
    println!("{:?}", v1); // error
}

You can't compile this code because v1 is not accessible in the print line.

error[E0502]: cannot borrow `v1` as immutable because it is also borrowed as mutable

Discussion

This means that the auto-generated closure mutably borrow on v1 and immutably on v2. The class is such like

struct ClosureEnv<'a,'b> {
    v1_mut_borrow: &'a mut Vec<i32>,
    v2_borrow: &'b Vec<i32>
}

and the compiler implement FnMut for this.

TODO

I couldn't find a blanket implementation which implements FnOnce for FnMut. We should have a definition like

impl <A,F> FnOnce<A> for F where F: FnMut<A>

in standard library.

There are blanket implementations for &F but not sure they are what I am looking for. I will write an another article when I find one.