rust泛型過度使用思考_rust

來源:互聯網
上載者:User
寫這篇文章的時候我只是用列印函數地址的方式來測試的,這個測試方法可能並不是很妥當,可能會引起編譯器在某些情況下放棄最佳化(個人猜測),目前想到的方法:應該在release模式下大量impl一個實現了靜態方法的trait看檔案膨脹的速度,這個我目前還在想辦法測試。所以目前來說下面的結論暫時還是存疑的,希望大家也能想辦法測試或者能直接找到官方的實際做法的文章。

目前瞭解來看rust泛型的概念很好,比c++多了很多類型限定和編譯時間檢查,出錯提示相對c++的模板來說也特別特別的友好。但任何事物總是有兩面性,過度的使用泛型會使得代碼極度的膨脹,這個膨脹的速度在我看來比c++的模板快多了 1 trait裡的預設函數和方法引起的代碼膨脹

首先來看一段簡單的代碼:

trait Foo{    fn foo(){        println!("foo");    }}struct Bar1;struct Bar2;impl Foo for Bar1{}impl Foo for Bar2{}fn main(){    println!("{:p}",&Bar1::foo);    println!("{:p}",&Bar2::foo);}

看看這個運行結果:

0x7f81474121500x7f8147412178

結果可以看到:Bar1::foo和Bar2::foo是兩個地址並不一樣的函數,這樣就意味著雖然這兩個函數的二進位代碼完全一樣,但是rust還是給複製了兩份。
從rust語言本身的特性來看這麼做有他的理由,比如預設函數可以被覆蓋等,但從結果上來看你就必須很小心了,如果你的trait Foo有很多預設函數,並且不巧你有很多類型都實現了Foo,那麼代碼的膨脹速度估計會出乎意料的 2 儲存closure引起的代碼膨脹

rust的closure與c++類似,都是產生一個匿名的結構體封裝你用到的“環境”裡所有的值,然後類似c++重載operator ()來實現調用,這也就意味著兩個完全相同的closure,比如:|i|i+100 ,你寫兩次他就是兩個不同的類型,這樣你用泛型儲存closure的時候就要注意了

下面看一個稍微複雜一點的例子:

trait Foo{    fn foo(){        println!("addr of Foo::foo:{:p}",&Self::foo);    }    fn print_foo_addr(&self);}//儲存closurestruct Bar<F:Fn(i32)->i32>{    func:F,    i:i32,}impl <F> Bar<F> where F:Fn(i32)->i32{    fn new(f:F)->Bar<F>{        Bar{            func:f,            i:100        }    }    fn print_new_addr(&self){        println!("addr of Bar::new :{:p}",&Self::new);        println!("addr of Bar::print :{:p}",&Self::print_new_addr);    }   }impl<F>Foo for Bar<F> where F:Fn(i32)->i32 {    fn print_foo_addr(&self){        Self::foo();        println!("addr of Foo::print:{:p}",&Self::print_foo_addr);    }}fn main(){    let b = Bar::new(|i|{        i+100    });    let b2 = Bar::new(|i|{        i+100    });    b.print_foo_addr();    b.print_new_addr();    println!("");    b2.print_foo_addr();    b2.print_new_addr();}

Play地址:http://is.gd/CM8Vnj
無論是debug,release,nightly輸出結果都差不多:

addr of Foo::foo:0x7ffbe66e5180addr of Foo::print:0x7ffbe66e5190addr of Bar::new :0x7ffbe66e51a0addr of Bar::print :0x7ffbe66e51b0addr of Foo::foo:0x7ffbe66e51c0addr of Foo::print:0x7ffbe66e51d0addr of Bar::new :0x7ffbe66e51e0addr of Bar::print :0x7ffbe66e51f0

可以看到所有的方法的代碼全部被複製了一遍

如果你只是用某個函數或者方法接收一個closure做回調那就好很多了,rust不是整個類型的全部函數方法都複製,rust只會把你接收closure的那個函數重新實現一下

附帶一個列子強化下即使看起來一樣的closure都是不一樣的:

struct Foo<F:Fn(i32)->i32>{    f:F,}impl<F:Fn(i32)->i32> Foo<F>{    fn new(f:F)->Foo<F>{        Foo{            f:f,        }    }}fn main(){    let b = Foo::new(|i|{i+100});    (b.f)(100);}

上面這個例子儲存了一個closure,編譯和運行都正常,play地址:http://is.gd/JVCd00

我們對其做一些修改,再添加一個成員:f2:F

struct Foo<F:Fn(i32)->i32>{    f:F,    f2:F,}impl<F:Fn(i32)->i32> Foo<F>{    fn new(f:F,f2:F)->Foo<F>{        Foo{            f:f,            f2:f2,        }    }}fn main(){    let b = Foo::new(|i|{i+100},|i|{i+100});    (b.f)(100);    (b.f2)(100);}

Play地址:http://is.gd/GSTQbq
這個列子是不能正確編譯的的,看看編譯結果:

<anon>:20:33: 20:43 error: mismatched types: expected `[closure <anon>:20:22: 20:32]`,    found `[closure <anon>:20:33: 20:43]`(expected closure,    found a different closure) [E0308]<anon>:20     let b = Foo::new(|i|{i+100},|i|{i+100});                                          ^~~~~~~~~~note: in expansion of closure expansion<anon>:20:33: 20:43 note: expansion site<anon>:20:33: 20:43 help: see the detailed explanation for E0308<anon>:20:33: 20:43 note: no two closures, even if identical, have the same type<anon>:20     let b = Foo::new(|i|{i+100},|i|{i+100});                                          ^~~~~~~~~~note: in expansion of closure expansion<anon>:20:33: 20:43 note: expansion site<anon>:20:33: 20:43 help: consider boxing your closure and/or using it as a trait object<anon>:20     let b = Foo::new(|i|{i+100},|i|{i+100});                                          ^~~~~~~~~~note: in expansion of closure expansion<anon>:20:33: 20:43 note: expansion siteerror: aborting due to previous errorplaypen: application terminated with error code 101Compilation failed.

也就是說我們看起來兩個完全一樣的closure編譯器認為是兩種類型 3 lifetime參數並不會引起代碼的複製

lifetime也放在了<>裡,測試下看看:

struct Foo<'a>{    i:&'a i32,}impl<'a> Foo<'a>{    fn print_addr(&self){        println!("i={},{:p}",self.i,&Self::print_addr);    }}fn main(){    let i=1i32;    let f1 = Foo{i:&i};    f1.print_addr();    {        {            let k=2i32;            let f3 = Foo{i:&k};            f3.print_addr();        }        let j=3i32;        let f2 = Foo{i:&j};        f2.print_addr();        {            let f4 = Foo{i:&i};            f4.print_addr();        }    }}

Play地址:http://is.gd/lepdI2
結果可以看出lifetime並不會引起代碼的複製

i=1,0x7f7ca62b1140i=2,0x7f7ca62b1140i=3,0x7f7ca62b1140i=1,0x7f7ca62b1140

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.