rust的函数指针与闭包|8月更文挑战

函数指针

rust中定义函数签名是显式制定的,保证了编译器的检查数组

  1. 函数项类型能够经过显式指定函数类型转换为一个函数指针类型
  2. 在写代码的时候,尽量地去使用函数项类型,不到万不得已不要使用函数指针类型,这样有助于享受零大小类型的优化。
let example  = demo; 
let c: fn(&str)->bool= example; // fn pointer type
assert_eq!(0,std::mem::size_of_val(&example));
assert_eq!(8,std::mem::size_of_val(&c));
复制代码

闭包

闭包能够捕获环境中的自由变量,这一点弥补了函数的缺陷markdown

fn fiber(x: i32)-> fn(i32)-> i32{
    fn add(y: i32)-> i32{
        x+y // error[E0434]: can't capture dynamic environment in a fn item
    }
    add
}
fn main() {
    let f1 = fiber(1);
    assert_eq!(2,f1(1));// help: use the `|| { ... }` closure form instead
}
复制代码

闭包的使用方法以下:闭包

fn fiber(x: i32) -> impl FnMut(i32)-> i32 {
    move |y| x+y
}
fn main() {
    assert_eq!(2,fiber(1)(1));
}
复制代码

rust中闭包的实现原理:

  1. 未捕捉环境变量 ----->全部权机制
  2. 捕捉变量并修改 ----->可变借用(&mut T)
  3. 捕捉变量但不修改 ----->不可变借用(&T )

rust全部权语义的核心理念能够归结为“可变不共享,共享不可变”的规则函数

​ ---《深刻浅出rust》优化

即:ui

  1. 若是没有捕获上下文的变量,则实现FnOncetrait
  2. 若是捕获了变量,但未对变量进行修改的实现FnMut
  3. 若是捕获了变量而且进行修改,则实现Fntrait

特殊状况:spa

  1. 编译器会将FnOnce看成fn(T)函数指针看待。
  2. Fn->FnMut->FnOnce三者的关系是依次继承。
#![feature(unboxed_closures, fn_traits)]
struct Closure{
    env_car: i32
}
impl FnOnce<()> for Closure{
    type Output = ();
    #[warn(unused_variables)]
    extern "rust-call" fn call_once(self, args: () ) -> (){
        println!("FnOnce{}",self.env_car)
    }
}
impl FnMut<()> for Closure{
    extern "rust-call" fn call_mut(&mut self, args: () ) ->(){
        println!("可变引用FnMut{}",self.env_car)
    }
}
impl Fn<()> for Closure{
    extern "rust-call" fn call(&self, args: () ) -> (){
        println!("不可变引用Fn{}",self.env_car)
    }
}

fn main() {
    let demo = 1;
    let c = Closure {
        env_car: demo
    };
    c.call(())
}
复制代码

若是要在函数内返回一个闭包,须要用到move关键字,这样会捕获函数内部变量,并转移该变量的全部权。若是是Sring类型这种没有实现Copy语义的类型,将不能捕获,move也会失效。这两种状况的闭包分别称为逃逸闭包和非逃逸闭包。指针

根据全部权的语义,不可变引用是能够同时存在多个的,可是在闭包里有一种状况只能存在惟一的不可变引用:code

let mut a = [1,2,3];
let x = &mut a;
{
    let mut c = || {(*x)[0] = 0;};
    let  y = &x; // 报错 second borrow occurs here
    c();
}
let z = &x; //ok
复制代码

在上面的代码里先定义一个固定长度的数组,接着取a的可变借用x,而后在一个表达式内部定义c是一个闭包,这个闭包的做用是将a的第一个元素修改成0,接着再取x的解引用,这里编译器报错显示cannot borrow x because previous closure reuqires unique access,意思是第一次的索引已经存在了x的不可变引用(隐式),对于x的第二次借用编译器便不容许对于闭包捕获变量的第二次不可变借用。orm

闭包的trait

trait做为rust的核心,闭包又实现了哪些trait

  1. 闭包都默认实现了Sizedtrait
  2. 若是全部捕获的变量都实现了Copytrait,而且以可变引用的方式修改它,这样闭包就不会有Copytrait。
  3. 若是捕获的变量带有Move语义,且闭包内有修改或者消耗该变量,则闭包不会有Copytrait。

以上的三点其实也都对应了rust的全部权语义,即不能同时存在多个可变引用。

相关文章
相关标签/搜索