日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

10玩rust_C++工程师的Rust迁移之路(5)- 继承与组合 - 下

發布時間:2023/12/4 c/c++ 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 10玩rust_C++工程师的Rust迁移之路(5)- 继承与组合 - 下 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

2020-11-25 更新:

  • 修正了C++ 20中的concept語法

  • 在上一篇文章 https://zhuanlan.zhihu.com/p/76740667 中,我介紹多態、靜態分發和動態分發的概念,以及他們各自在C++和Rust中的實現方式。

    在本文中,我會重點講Rust中的Trait實現的靜態分發與C++ 20(準確的說,現在還叫做C++ 2a)中的concepts的區別。

    在具體介紹這個區別之前,我想跟大家介紹一個概念,叫做duck typing(鴨子類型)。

    鴨子類型

    呃……你沒有看錯,這個鴨子就是你平常理解的那個鴨子,我也沒有翻譯錯……

    鴨子類型[1]是鴨子測試的一個應用:

    如果它走起來像鴨子,也跟鴨子一樣發出嘎嘎的叫聲,那么它就是鴨子

    聽起來似乎非常無厘頭,但這個模式實際上被廣泛的應用于多種語言。

    在C++中的應用

    template <typename T> concept Stream = requires(T a, std::uint8_t* mut_buffer, size_t size, const std::uint8_t* buffer) {{ a.read(mut_buffer, size) } -> std::convertible_to<size_t>;{ a.write(buffer, size) } -> std::convertible_to<size_t>; };class Console { ... }; class FileStream { ... };

    在Golang中的應用

    type Stream interface {Read(uint32) []byteWrite([]byte) uint32 }type Console struct { ... } type FileStream struct { ... }func (c Console) Read(size uint32) []byte {... }func (c Console) Write(data []byte) uint32 {... }

    在上面的兩個例子中,我們可以注意到,Console和FileStream這兩個類型都沒有顯示的聲明自己兼容Stream concept(interface),但在編譯階段,編譯器可以根據他們實現的方法來判斷他們支持Stream要求的操作,從而實現多態。

    這個功能看似非常誘人,省去了顯式聲明的麻煩,但也帶來了問題。

    鴨子類型的局限性

    程序員的造詞能力通常是非常匱乏的(大家每次要給變量命名時的抓耳撓腮可以證明這一點),所以非常容易在方法名上重復,但在兩個語境中又可能具有完全不同的語義。

    舉個例子:

    template <typename T> concept Thread = requires(T a, int signal) {{ a.kill(signal) }; };class DuckFlock { public:void kill(int amount); };void nofity_thread(Thread& t) {t.kill(SIGUSR1); }

    原本我以為給鴨群發了一個信號,讓它們打印一下狀態,結果一不小心就殺掉了10只鴨子[2],真的只能召喚華農兄弟了。

    Rust的設計

    在Rust中,是不允許這種情況出現的,必須顯式的生命類型實現的是哪個trait:

    trait Thread {fn kill(&mut self, signal:i32); }trait Flock {fn kill(&mut self, amount:i32); }struct DuckFlock {ducks: i32 }impl DuckFlock {pub fn new(amount: i32) -> DuckFlock {DuckFlock{ ducks: amount }} }impl Thread for DuckFlock {fn kill(&mut self, signal: i32) {if signal == 10 {println!("We have {} ducks", self.ducks);} else {println!("Unknown signal {}", signal);}} }impl Flock for DuckFlock {fn kill(&mut self, amount: i32) {self.ducks -= amount;println!("{} ducks killed", amount);} }fn main() {let mut flock = DuckFlock::new(100);{let thread:&mut Thread = &mut flock;thread.kill(10);}{let flock:&mut Flock = &mut flock;flock.kill(10);}{let thread:&mut Thread = &mut flock;thread.kill(10);} }

    同樣的,這個例子我也放到Rust Playground,歡迎大家前去玩耍。

    Markers

    在Rust中,由于實現Trait必須要顯式聲明,這就衍生出了一種特殊類型的trait,它不包含任何的函數要求:

    trait TonyFavorite {} trait Food {fn name(&self) -> String; }struct PeikingDuck;impl Food for PeikingDuck {fn name(&self) -> String {"Peiking Duck".to_owned()} }impl TonyFavorite for PeikingDuck {}struct Liver;impl Food for Liver {fn name(&self) -> String {"Liver".to_owned()} }fn eat<T: Food + TonyFavorite>(food: T) {println!("Tony only eat his favorite food like {}", food.name()); }fn main() {eat(PeikingDuck);// eat(Liver); // compile error }

    這里例子的Playground在此。

    事實上,在Rust中,類似的Marker還有非常多,比如Copy、Sync、Send等等。在后續的文章中,再跟大家逐一解釋這些trait的含義與妙用。

    在下一節的文章中,我會介紹Rust類型系統和C++類型系統最大的不同之一:Rust結構體不能繼承,以及為什么。敬請期待。

    延伸閱讀

    上一篇

    黃玨珅:C++工程師的Rust遷移之路(4)- 繼承與組合 - 中?zhuanlan.zhihu.com

    下一篇

    黃玨珅:C++工程師的Rust遷移之路(6)- 繼承與組合 - 后?zhuanlan.zhihu.com

    參考

  • ^Duck typing?https://en.wikipedia.org/wiki/Duck_typing
  • ^在Linux下SIGUSR1等于10
  • 總結

    以上是生活随笔為你收集整理的10玩rust_C++工程师的Rust迁移之路(5)- 继承与组合 - 下的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。