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的全部權語義,即不能同時存在多個可變引用。

相關文章
相關標籤/搜索