当前位置:网站首页>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
边栏推荐
- undefined reference to `Nabo::NearestNeighbourSearch
- C1 notes [task training part 2]
- Basic usage of crawler requests
- k8s之实现redis一主多从动态扩缩容
- Flash - Middleware
- MySQL auto start settings start with systemctl start mysqld
- Crawl the product data of cicada mother data platform
- 2022 Jiangxi Photovoltaic Exhibition, China distributed Photovoltaic Exhibition, Nanchang solar energy utilization Exhibition
- [UDS unified diagnostic service] IV. typical diagnostic service (4) - online programming function unit (0x34-0x38)
- Queue solving Joseph problem
猜你喜欢
MySQL_ 01_ Simple data retrieval
ArcGIS license error -15 solution
mysql自动启动设置用Systemctl start mysqld启动
[UDS unified diagnostic service] (Supplement) v. detailed explanation of ECU bootloader development points (1)
解决报错max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
MySQL 中的字符串函数
Implementation of k8s redis one master multi slave dynamic capacity expansion
An example of linear regression based on tensorflow
Remember using Ali Font Icon Library for the first time
cv_ Solution of mismatch between bridge and opencv
随机推荐
Selenium + phantom JS crack sliding verification 2
Docker 安装 MySQL
C network related operations
YOLOv4剪枝【附代码】
Utilisation de la liste - Ajouter, supprimer et modifier la requête
Build openstack platform
MySQL auto start settings start with systemctl start mysqld
7-21 wrong questions involve knowledge points.
Using files to save data (C language)
Romance in C language
C language implements memcpy, memset, strcpy, strncpy, StrCmp, strncmp and strlen
2022 judgment questions and answers for operation of refrigeration and air conditioning equipment
Solving the problem of displaying too many unique values in ArcGIS partition statistics failed
re正則錶達式
Crawl lottery data
I / O multiplexing and its related details
ES6 face test questions (reference documents)
Implementation of object detection case based on SSD
2022 Jiangxi energy storage technology exhibition, China Battery exhibition, power battery exhibition and fuel cell Exhibition
The ultimate experience, the audio and video technology behind the tiktok