RUST Learning Record -> Abschluss


Vorwort

Nachdem ich kürzlich die Grundkenntnisse von Rost erlernt hatte, begann ich, Kenntnisse im Zusammenhang mit der Netzwerkprogrammierung zu erlernen. Bei der Implementierung eines einfachen Multithread-Webservers stellte ich fest, dass dieser viele abschlussbezogene Kenntnisse enthielt, also ergänzte ich das Studium nach dem Unterricht und machte sich Notizen.

Referenz: B-Station Linghu Yichong Rost fortgeschritten


1. Was ist ein Verschlussverschluss?

Abschlüsse sind anonyme Funktionen, die in Variablen gespeichert oder als Parameter an andere Funktionen übergeben werden können. Der Unterschied zwischen Abschlüssen und Funktionen besteht darin, dass Abschlüsse die Erfassung von Werten im Bereich des Aufrufers ermöglichen.

Die Definition ist relativ abstrakt und es ist intuitiver, direkt aus dem Code zu lernen.

Zweitens: Verwenden Sie einen Verschluss

1. Definieren Sie den Abschluss

Code wie folgt anzeigen:

	//闭包的完整定义:	
	let add_one_v2 = |x:u32| -> u32 {
    
    x+1};
    //闭包定义会为每个参数和返回值推导一个具体的类型
    //但是不能推导两次
    //简化定义:
    let add_one_v3 = |x| {
    
    x+1};
    let add_one_v4 = |x| x+1;

Der Abschluss ähnelt tatsächlich der Definition einer Funktion. Hier definieren wir eine Funktion mit ähnlichen Funktionen zum Vergleich:

fn add_one_v1 (x:u32) -> u32 {
    
    
    x+1
}

Darüber hinaus werden Beispiele für die Unfähigkeit gegeben, Typen zweimal abzuleiten:

//不能推导两次的例子:
     let example_closure = |x| x;
      
     let a = example_closure("okokok".to_string());
     println!("{}",a);
     let b = example_closure(5);
     println!("{}",b);
    //上述例子中,在为a赋值时
    //闭包example_closure()进行了第一次类型推导
    //参数类型和返回值类型都是String类型
    //在为b赋值时
    //闭包example_closure()进行了第二次类型推导
    //参数类型和返回值类型都是integer类型

    //在编译时发生报错
    //为b赋值时期望参数x是一个String类型
    //但是传入的是integer类型
    //所以闭包可以自行推断自己的参数和返回值类型,
    //但是不能判断两次

Darüber hinaus erwähnt die Definition, dass Abschlüsse Werte im Aufruferbereich erfassen können.
Beispiele sind wie folgt:

//捕获作用域中的值
    let i = 4;
    let exe = |x| x+i;
    let test = exe(5);
    println!("{}",test);

Es ist ersichtlich, dass die im Bereich definierte Ganzzahlvariable, die ich im Bereich definiert habe, direkt im Abschluss verwendet werden kann, was die obige Aussage bestätigt, dass „der Abschluss den Wert im Bereich des Aufrufers erfassen kann“.

2. Verschlüsse verwenden

In Bezug auf die allgemeine Verwendung von Abschlüssen und Bereichswerten für die Abschlusserfassung wurden oben einfache Beispiele angegeben, daher werde ich hier nicht auf Details eingehen. Als Nächstes werde ich die Implementierung der Cache-Funktion anhand eines Beispiels veranschaulichen. Das Beispiel sieht wie folgt aus:

//首先定义一个结构体
//->前置知识:泛型,trait
struct Cacher<T>
    where T:Fn(u32) -> u32
    //定义T的trait bound,指定类型T必须拥有如上所述的闭包特性
{
    
    
    calculation:T,
    //这里相当于在结构体中内置了一个闭包
    value:Option<u32>,
}

impl<T> Cacher<T> where T:Fn(u32) -> u32 {
    
    
    fn new(calculation:T) -> Cacher<T> {
    
    
    //初始化结构体
        Cacher {
    
    
            calculation,
            //结构体知识:当拥有同名变量时可以直接定义
            value:None,
        }
    }

    fn value(&mut self, arg:u32) -> u32 {
    
    
        //此处实现了缓存的功能
        //如果结构体中的value值为空
        //则传入一个经过内置闭包计算后的值并保存
        //如果结构体中的value值不为空
        //则保持原来的值不变并将初始值传回
        match self.value {
    
    
            Some(v) => v,
            //初始值不为空,将初始值传回
            None => {
    
    
            //初始值为空
                let v = (self.calculation)(arg);
                //将传入的新值经过内置闭包计算
                self.value = Some(v);
                //写入结构体中(即写入缓存)
                v
                //传回写入实际值
            },
        }
    }
}

Als nächstes definieren Sie die Hauptfunktion zum Testen:

fn main() {
    
    
    let calculation = |x| x+1;
    let mut tmp = Cacher::new(calculation);
    //注意这里要将结构体定义为可变类型
    let v1 = tmp.value(1);
    //此时结构体中value为空
    //所以会写入经过内置闭包计算的新值并传回
    println!("v1 is {}",v1);
    let v2 = tmp.value(2);
    //此时结构体中已经有value值
    //所以不会写入新值,只会将初始值传回
    println!("v2 is {}",v2);
    println!("Hello, world!");
}

Das Ergebnis ist wie folgt:

   Compiling learn_closure2 v0.1.0 (/root/learn_rust/learn_closure2)
    Finished dev [unoptimized + debuginfo] target(s) in 1.58s
     Running `target/debug/learn_closure2`
v1 is 2
v2 is 2
Hello, world!

Dies ist aus den Ergebnissen des Kompilierens und Ausführens ersichtlich

3. Erfassen Sie den Bereichswert erneut

Schließungen können ihre Umgebung auf drei Arten erfassen, entsprechend den drei Arten, Parameter einer Funktion zu erhalten: Eigentumserlangung, veränderliche Kreditaufnahme und unveränderliche Kreditaufnahme.

Nach einigen eigenen Versuchen stellte ich fest, dass der Rust-Compiler standardmäßig eine unveränderliche Ausleihe des erfassten Werts vornimmt, wenn move nicht explizit deklariert wird. Das Experiment der Variablenausleihe war jedoch nach mehreren Versuchen nicht erfolgreich. Ich werde es nach der Vorbereitung tun. diskutieren. Daher werden im Folgenden nur Beispiele erfolgreicher Experimente aufgeführt und in Zukunft ergänzt.

Der erste ist ein einfacher Erfassungstest für Variablen mit dem Kopiermerkmal:

	let x = 3;
    let equal_to_x = |z| z==x;
    let z1 = 3;
    assert!(equal_to_x(z1));

Die Testergebnisse sind wie folgt:

   Compiling learn_closure3 v0.1.0 (/root/learn_rust/learn_closure3)
    Finished dev [unoptimized + debuginfo] target(s) in 0.32s
     Running `target/debug/learn_closure3`

Ähnliche Experimente wurden auch für Variablen ohne Kopiermerkmal durchgeführt, beispielsweise für den String-Typ. Der Code lautet wie folgt:

	let x = "123".to_string();
    let equal_to_x = |z| z==x;
    println!("{}",x);
    let z2 = String::from("123");
    assert!(equal_to_x(z2));

Die Testergebnisse sind wie folgt:

Compiling learn_closure3 v0.1.0 (/root/learn_rust/learn_closure3)
    Finished dev [unoptimized + debuginfo] target(s) in 0.31s
     Running `target/debug/learn_closure3`
123

Da x nach der Erfassung immer noch im Hauptbereich wirksam werden kann, kann davon ausgegangen werden, dass die Standarderfassung hier eine unveränderliche Referenz ist.
Wenn sie als Verschiebung deklariert wird, sieht sie wie folgt aus:

let x = "123".to_string();
    let equal_to_x = move |z| z==x;
    println!("{}",x);
    let z2 = String::from("123");
    assert!(equal_to_x(z2));

Die Testergebnisse sind wie folgt:

Compiling learn_closure3 v0.1.0 (/root/learn_rust/learn_closure3)
error[E0382]: borrow of moved value: `x`
  --> src/main.rs:11:19
   |
9  |     let x = "123".to_string();
   |         - move occurs because `x` has type `String`, which does not implement the `Copy` trait
10 |     let equal_to_x = move |z| z==x;
   |                      --------    - variable moved due to use in closure
   |                      |
   |                      value moved into closure here
11 |     println!("{}",x);
   |                   ^ value borrowed here after move

For more information about this error, try `rustc --explain E0382`.

Es kann festgestellt werden, dass der klassische Fehler „ Wert nach Verschiebung hier entliehen “ auftritt, was darauf hinweist, dass der Besitz in der Schließung erfasst wurde.


Zusammenfassen

Das Obige sind einige meiner Lernaufzeichnungen über Schließungen. Ich werde die Erfassungstypen in Zukunft weiter studieren und diskutieren. Wenn es Fehler gibt, können Sie mich gerne kritisieren und aufklären.

Supongo que te gusta

Origin blog.csdn.net/weixin_45704680/article/details/121094813
Recomendado
Clasificación