rust的几种闭包类型

前提知识:

rust里面有move,copy,clone。
所有对象都有一个类型,具体所有权。
比如
#[derive(Debug)]
struct Complex {
    real: f64,
    imag: f64,
}
fn main() {
    let a = Complex{real:1.,imag:2.};
    let b = a;
    println!("{:?}",a);
    println!("{:?}",b);
}
会提示 println!("{:?}",a); ^ value borrowed here after move,因为b=a,会转移a的所有权到b,这就是move语义。
但是,我们把#[derive(Debug)] 改成 #[derive(Debug,Clone,Copy)]就可以编译通过运行了。
Complex的各种成员都是Clone,所以Complex可以派生实现Clone
Complex的各种成员都是Copy,所以Complex可以派生实现Copy
实现Copy以后,函数传参,或者b=a这样的操作,就不会转移所有权了。
Copy就是一个浅拷贝,只是把a这个结构体的数据完全复制了一遍,如果a里面一个成员是&strstr指向的是堆数据,copy以后,b也可以访问那个堆数据。
Clone是深拷贝,比如String.Clone,就会在堆上复制一模一样的字符串数据,构建新的String,指向那片内存
#![feature(unboxed_closures)]
#![feature(fn_traits)]
#[derive(Debug,Clone)]
struct OnceStr {
    s:String,
}
impl OnceStr {
    fn new(s: String) -> Self {
        OnceStr { s }
    }
}
impl FnOnce<(&str,)> for OnceStr {
    type Output =();
    extern "rust-call" fn call_once(self, s: (&str,)) -> Self::Output {
        println!("this is in FnOnce {}",self.s);
        let mut ss = self.s;
        ss.push_str(s.0);
        let os = Self::new(ss);
        println!("FnOnce {:?}",os);
    }
}
impl FnMut<(&str,)> for OnceStr {
    extern "rust-call" fn call_mut(&mut self, s: (&str,)) -> Self::Output {
        self.s.push_str(s.0);
        println!("this is in FnMut {}",self.s);
    }
}
impl Fn<(&str,)> for OnceStr {
    extern "rust-call" fn call(&self, s: (&str,)) -> Self::Output {
        println!("get {} this is in Fn {}",s.0,self.s);
    }
}
fn main() {
    let s = "hello".to_string();
    let mut c = OnceStr::new(s);
    c.clone().call_once(("aa",));
    c.call_mut(("aa",));
    c.call(("aa",));
    c.call(("bb",));
    c.call_mut(("cc",));
    {
        let mut s = "hello".to_string();
        let mut c1 = move || {
            s.push_str("!");
            println!("c1 is called {s}");
        };
        call_once(c1.clone());
        // call_once(c1);
        call_mut(&mut c1);
        call_mut(&mut c1);
        call_mut(&mut c1);
        c1.call_mut(());    //这种调用方法和OnceStr没有区别
        c1.call_once(());
        // test(&c1);   
    }
    {
        let mut s = "hello".to_string();
        let c2 = || {
            s.push_str("=");
            println!("c2 {}",s);
        };
        call_once(c2);  //-- value moved here
        let mut c3 = || {
            s.push_str("=");
            println!("c3 {}",s);
        };
        call_mut(&mut c3);
        call_mut(&mut c3);
        c3.call_mut(());//这种调用方法和OnceStr没有区别
        c3.call_mut(());
        c3.call_once(());
    }
    {
        let mut s = "hello".to_string();
        let mut c3 = || {
            println!("{}",s);
        };
        call_once(c3.clone());
        call_once(c3);
        call_once(c3);
        call_mut(&mut c3);
        call_mut(&mut c3);
        call_mut_copy(c3);
        call_mut_copy(c3);
        call_fn(&c3);
        call_fn(&c3);
        call_fn_copy(c3);
        call_fn_copy(c3);
        c3.call_once(());//这种调用方法和OnceStr没有区别
        c3.call_mut(());
        c3.call(());    //普通闭包当然也可以直接调用Trait的方法。
    }
    // call_fn(&||c("aa"));
    // call_fn(&||c("aa"));
    call_one(&c);   //这种调用方式和普通的闭包没有区别
    call_two(&mut c);
    // call_three(c);
    call_three(c.clone());
    call_two(&mut c);

}

fn call_one<F: Fn(&str)>(f:&F) {
    f("one");
}
fn call_two<F: FnMut(&str)>(f:&mut F) {
    f("two");
}
fn call_three<F: FnOnce(&str)>(f:F) {
    f("three");
}
// 编译不能通过,传入引用,不能转移所有权
// fn test<F:FnOnce()>(f:&F) ->F::Output {
//     (*f)()
// }
fn call_once<F:FnOnce()>(f:F) ->F::Output {
    f()
}
fn call_mut_copy<F:FnMut()>(mut f:F)->F::Output {
    f()
}

fn call_fn_copy<F:Fn()>(f:F)->F::Output {
    f()
}
fn call_fn<F:Fn()>(f:&F)->F::Output {
    f()
}
fn call_mut<F:FnMut()>(f:&mut F)->F::Output {
    f()
}

rust的闭包可以近似理解成一个匿名的结构体。闭包捕获的变量,就是结构体的成员

例如
let mut s = "hello".to_string();
let c1 = move || {
    s.push_str("!");
    println!("c1 can be called once {s}");
};
c1在创建的时候,有一个String变量,类似于上面的结构体OnceStr,同时,C1是FnOnce,也是FnMut
由于String实现了Clone,所以C1也是可以Clonecall_once(c1.clone());
// call_once(c1);
call_mut(&mut c1);
call_mut(&mut c1);

对于
let mut c3 = || {
    s.push_str("=");
    println!("c3 {}",s);
};
call_mut(&mut c3);
C3类似于一个匿名结构体,结构体里面有一个&mut String,既没有Copy,也没有Clone,同时是FnOnce,也是FnMut
调用call_once就会丧失所有权,但是可以多次调用call_mut进行修改

对于
let mut s = "hello".to_string();
let c3 = || {
    println!("{}",s);
};
c3类似于只有&str的匿名结构体,既是Clone,也是Copy的。所以它可以被各种调用
posted @   念秋  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示