rust闭包
参考
Rust有三个闭包trait:Fn、FnMut和FnOnce,编译器会根据闭包内代码的行为自动为闭包实现这些trait。
上面这段话超级重要!!!
- 对于不可变或移动捕获变量的闭包,编译器会实现Fn trait,这样闭包可以多次调用,甚至可以在多个线程上并行调用。
- 对于可变捕获变量但不移动它们出闭包的闭包,编译器会实现FnMut trait,这样的闭包可以多次调用,但不能并行调用。
- 对于会移动捕获变量出闭包的闭包,编译器会实现FnOnce trait,这样的闭包只能调用一次。注意move关键字不影响trait实现,它只是强制移动变量进入闭包。
Fn trait是FnMut的子trait,FnMut trait是FnOnce的子trait。这意味着:
- 每个实现Fn trait的闭包也实现了FnMut和FnOnce。任何需要FnMut或FnOnce的地方,都可以传递实现Fn的闭包。
- 每个实现FnMut trait的闭包也实现了FnOnce。任何需要FnOnce的地方都可以传递实现FnMut的闭包。
如果被调用者接受FnOnce,它对闭包能做的事情受到最大限制——它只能调用闭包一次。但是调用者可以自由传递任何闭包,因为所有的闭包都实现了FnOnce,这是由子trait和父trait的关系决定的。
如果被调用者接受FnMut,那么它对闭包的限制会少一些。现在它可以调用闭包多次,但是仍然不能例如在多个线程上并发地调用闭包,因为那会导致数据共用。相应的,调用者也会受到更多的限制。例如,它不能在闭包中drop一个被捕获的变量或以其他方式将其移出闭包。
最后,如果被调用者接受Fn,那么它可以无限制的调用闭包,甚至并发调用。与此同时,调用者不能改变闭包中的变量。
最后附上一个学习闭包过程中,关于函数调用不可变String作为形参可变String的例子。
let a = "fox".to_string();
let add1=&a;
println!("{:p}",add1); // 打印变量的地址
let b = test1(a);
println!("{:?}",b);
println!("{:p}",&b);
}
fn test1(mut s : String)-> String{
let add2 = &s;
println!("{:p}",add2);
s.push_str("123");
s
}
PS D:\code\rust\test_demo> cargo run
Compiling test_demo v0.1.0 (D:\code\rust\test_demo)
Finished dev [unoptimized + debuginfo] target(s) in 2.51s
Running `target\debug\test_demo.exe`
0x119a6ff3a8
0x119a6ff420
"fox123"
0x119a6ff408
let a = "fox".to_string();
let add1=&a;
println!("{:p}",add1); // 打印变量的地址
let b = test1(a);
println!("{:?}",b);
println!("{:p}",&b);
}
fn test1(s : String)-> String{
let add2 = &s;
println!("{:p}",add2);
// s.push_str("123");
s
}
PS D:\code\rust\test_demo> cargo run
Compiling test_demo v0.1.0 (D:\code\rust\test_demo)
Finished dev [unoptimized + debuginfo] target(s) in 0.42s
Running `target\debug\test_demo.exe`
0x8758aff458
0x8758aff4d0
"fox"
0x8758aff4b8
实际上a是不可变string,但是test1函数可以捕获a的所有权,然后重新给了一个新的变量s,s是可变的。打印地址可以看出他们的地址都不相同。