网站后台文章添加成功 不显示,注册安全工程师是干什么的,做一个公司网站的费用,哈尔滨建站的系统2020-11-25 更新#xff1a;修正了C 20中的concept语法在上一篇文章 https://zhuanlan.zhihu.com/p/76740667 中#xff0c;我介绍多态、静态分发和动态分发的概念#xff0c;以及他们各自在C和Rust中的实现方式。在本文中#xff0c;我会重点讲Rust中的Trait实现的静态分发…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_tosize_t;{ a.write(buffer, size) } - std::convertible_tosize_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中是不允许这种情况出现的必须显式的生命类型实现的是哪个traittrait 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 eatT: 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