当前位置:网站首页>Closure type of rust (difference between FN, fnmut and fnone)
Closure type of rust (difference between FN, fnmut and fnone)
2022-04-23 18:04:00 【Xu Yeping】
Rust Closure type of (Fn, FnMut, FnOne The difference between )
1 What is a closure ?
Let's take a look at the description on Wikipedia :
In computer science , Closure ( English :Closure), Also known as lexical closure (Lexical Closure) Or function closure (function closures), It's a function that references a free variable . This referenced free variable will exist with this function , Even if it has left the environment in which it was created . therefore , Another way of saying is that a closure is an entity composed of a function and its associated reference environment . Closures can have multiple instances at run time , Different reference environments and the same combination of functions can produce different instances .
The concept of closure appears in 60 years , The first programming language to implement closures was Scheme. after , Closures are widely used in functional programming languages such as ML Language and LISP. Many imperative programming languages have also begun to support closures .
You can see , The first sentence has explained what a closure is : A closure is a function that references a free variable . therefore , Closure is a special function .
stay Rust in , Closures are divided into three types , List the following
Fn(&self)
FnMut(&mut self)
FnOnce(self)
stay rust in , Functions and closures are implemented Fn
、FnMut
or FnOnce
Trait (trait
) The type of . Any type of object that implements one of these three qualities , All are Callable object , Can pass like functions and closures name()
Form call of ,()
stay rust Is an operator , Operator in rust Can be overloaded in .rust Operator overloading is achieved by implementing the corresponding trait
To achieve , and () The corresponding of the operator trait
Namely Fn
、FnMut
and FnOnce
, therefore , Any implementation of these three trait
One of the types of , In fact, it's overloaded ()
The operator .
2 Rust Definitions of three closures in :
2.1 FnOnce
- Standard library definition
#[lang = "fn_once"]
pub trait FnOnce<Args> {
type Output;
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
Parameter type is self
, therefore , This type of closure takes ownership of variables , A lifecycle can only be the current scope , Then it will be released .
- FnOnce Example :
#[derive(Debug)]
struct E {
a: String,
}
impl Drop for E {
fn drop(&mut self) {
println!("destroyed struct E");
}
}
fn fn_once<F>(func: F) where F: FnOnce() {
println!("fn_once begins");
func();
println!("fn_once ended");
}
fn main() {
let e = E {
a: "fn_once".to_string() };
// Add one like this move, See how the output order of program execution is different
// let f = move || println!("fn once calls: {:?}", e);
let f = || println!("fn once closure calls: {:?}", e);
fn_once(f);
println!("main ended");
}
The results are as follows :
fn_once begins
fn once closure calls: E {
a: "fn_once" }
fn_once ended
main ended
destroyed struct E
- FnOnce Type closure -Rust playground Example
But if the closure runs twice , such as :
fn fn_once<F>(func: F) where F: FnOnce() {
println!("fn_once begins");
func();
func();
println!("fn_once ended");
}
The compiler will report an error , Like this :
error[E0382]: use of moved value: `func`
--> src/main.rs:15:5
|
12 | fn fn_once<F>(func: F) where F: FnOnce() {
| - ---- move occurs because `func` has type `F`, which does not implement the `Copy` trait
| |
| consider adding a `Copy` constraint to this type argument
13 | println!("fn_once begins");
14 | func();
| ---- value moved here
15 | func();
| ^^^^ value used here after move
error: aborting due to previous error
FnOnce
Type closure error -Rust playgroound Example
Why is that ?
Or go back FnOnce
The definition of , Parameter type is self
, So in func
After the first execution , The variables that were captured before were released , So it's impossible to perform the second time . therefore , If you want to run multiple times , have access to FnMut\Fn
.
2.2 FnMut
- Standard library definition
#[lang = "fn_mut"]
pub trait FnMut<Args>: FnOnce<Args> {
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
Parameter type is &mut self, therefore , This type of closure is mutable , Will change the variable , But the variable will not be released . So you can run multiple times .
FnMut
Example
Similar to the above example , There are two places to change :
fn fn_mut<F>(mut func: F) where F: FnMut() {
func();
func();
}
// ...
let mut e = E {
a: "fn_once".to_string() };
let f = || {
println!("FnMut closure calls: {:?}", e); e.a = "fn_mut".to_string(); };
// ...
The results are as follows :
fn_mut begins
fn mut closure calls: E {
a: "fn_mut" }
fn mut closure calls: E {
a: "fn_mut" }
fn_mut ended
main ended
destroyed struct E
FnMut Type closure -Rust playground Example
It can be seen that FnMut Closures of types can be run multiple times , And you can modify the value of the capture variable .
2.3 Fn
- Standard library definition
#[lang = "fn"]
pub trait Fn<Args>: FnMut<Args> {
extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}
Parameter type is &self
, therefore , This type of closure is immutable , Does not change variables , This variable will not be released . So you can run multiple times .
- Fn Example
fn fn_immut<F>(func: F) where F: Fn() {
func();
func();
}
// ...
let e = E {
a: "fn".to_string() };
let f = || {
println!("Fn closure calls: {:?}", e); };
fn_immut(f);
// ...
The results are as follows :
fn_imut begins
fn closure calls: E {
a: "fn" }
fn closure calls: E {
a: "fn" }
fn_imut ended
main ended
destroyed struct E
It can be seen that Fn Closures of types can be run multiple times , But you can't modify the value of the capture variable .
Fn Type closure -Rust playground Example
3 Common mistakes
Sometimes in use Fn/FnMut Here is the type of closure , Compilers often give such errors :
# ...
cannot move out of captured variable in an Fn(FnMut) closure
# ...
See how to reproduce this situation :
fn main() {
fn fn_immut<F>(f: F) where F: Fn() -> String {
println!("calling Fn closure from fn, {}", f());
}
let a = "Fn".to_string();
fn_immut(|| a); // The closure returns a string
}
In this way, there will be the above error . But how to fix it ?
fn_immut(|| a.clone());
But what is the reason ?
Just change the above code a little , To run a , The error given by the compiler is obvious :
fn main() {
fn fn_immut<F>(f: F) where F: Fn() -> String {
println!("calling Fn closure from fn, {}", f());
}
let a = "Fn".to_string();
let f = || a;
fn_immut(f);
}
The error given by the compiler is as follows :
7 | let f = move || a;
| ^^^^^^^^-
| | |
| | closure is `FnOnce` because it moves the variable `a` out of its environment
| this closure implements `FnOnce`, not `Fn`
8 | fn_immut(f);
| -------- the requirement to implement `Fn` derives from here
You see , The compiler deduces that the closure is FnOnce Type of , Because the closure finally returns a, Returned ownership , You can't run it a second time , Because closures are no longer a Owner .
and Fn
/FnMut
It is recognized that it can run multiple times , If the ownership of the captured variable is returned , Then it won't run next time , So it will report the previous error .
4 Conclusion
Because closures and rust Life cycle in , Ownership is closely linked , Sometimes it's hard to understand , But write more code , Try a few times more , You can probably understand the difference between the three .
All in all , Closure is rust Very easy to use functions in , It can make the code concise and elegant , It is worth learning and mastering !
Link to the original text :https://www.cnblogs.com/dream397/p/14190206.html
Original author :tycoon3
版权声明
本文为[Xu Yeping]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204230544498298.html
边栏推荐
- 2022 tea artist (primary) examination simulated 100 questions and simulated examination
- Reptile efficiency improvement method
- journal
- Basic usage of crawler requests
- Go file operation
- Climbing watermelon video URL
- Gobang game based on pyGame Library
- re正則錶達式
- Installation du docker redis
- MySQL_ 01_ Simple data retrieval
猜你喜欢
随机推荐
Remember using Ali Font Icon Library for the first time
Cloud native Virtualization: building edge computing instances based on kubevirt
Nanotechnology + AI enabled proteomics | Luomi life technology completed nearly ten million US dollars of financing
Process management command
Scikit learn sklearn 0.18 official document Chinese version
纳米技术+AI赋能蛋白质组学|珞米生命科技完成近千万美元融资
Map basemap Library
Re expression régulière
Docker 安装 Redis
Excel opens large CSV format data
C#的随机数生成
re正则表达式
Using files to save data (C language)
proxy server
2022江西光伏展,中國分布式光伏展會,南昌太陽能利用展
Install pyshp Library
Halo open source project learning (II): entity classes and data tables
Summary of floating point double precision, single precision and half precision knowledge
MySQL_ 01_ Simple data retrieval
idea中安装YapiUpload 插件将api接口上传到yapi文档上