首页 > 数据库 > 数据库开发技术 > 【投稿】北海 - Rust与面向对象(二)
/// 一个父类A
struct A {
...
}
impl A {
fn do_step1_common(&mut self) { ... }
// 缺省实现,留给子类实现,如果是C++/Java这类面向对象语言,很容易。若是Rust,该怎么搞?
fn do_step2_maybe_different(&mut self) { ... }
fn do_step3_common(&mut self) { ... }
pub fn do_all_steps(&mut self) {
self.do_step1_common();
self.do_step2_maybe_different();
self.do_step3_common();
}
}
// 具体的某个子类实现
struct A1 {
a: A,
...
}
impl A1 {
// 开始实现
fn do_step2_maybe_different(&mut self) {
// A1提供一种实现
}
}
trait DoStep2 {
fn do_step2_maybe_different(&mut self);
}
/// 另一个父类B
struct B<T: DoStep2> {
t: T, // 或者Box<&dyn DoStep2>
...
}
impl<T> B<T> {
fn do_step1_common(&mut self) { ... }
fn do_step3_common(&mut self) { ... }
}
impl<T: DoStep2> B<T> {
pub fn do_all_steps(&mut self) {
self.do_step1_common();
self.t.do_step2_maybe_different();
self.do_step3_common();
}
}
/// 具体的子类实现
struct B1 {
...
}
impl DoStep2 for B1 {
fn do_step2_maybe_different(&mut self) {
// B1提供一种实现
}
}
// 这样,
// B<B1> 相当于面向对象中的 A1
// B<B2> 相当于面向对象中的 A2
A、B都不是完整的父类实现,A1、B<B1>才是真正的具体类型,且它们都包含了父类的结构,虽然B<B1>的写法有点不合常规。若子类还拥有自己的独立的扩展结构的话,那Rust这种方式更优雅一些,拆分的更原子、更合理。实践中,往往不会这么完美的套用,会复杂很多,比如子类作为具体类型,想访问父类的成员,才能配合完成do_step2,Rust又该怎么做?面向对象的this指针则轻松支持。Rust不可能让B1再直接包含B,那样循环包含了,只能用引用或者指针来存在B1里面,但这样的话,岂不是太麻烦了,循环引用/包含都是我们极力避免的东西,麻烦到都想放弃模板方法了!B1实现DoStep2 trait的时候,self指向的仅仅是B1,并不知道B的存在。那怎么办?得让self指向整体B<B1>,那为B<B1>实现DoStep2行不行?像下面这样:impl DoStep2 for B<B1> {
fn do_step2_maybe_different(&mut self) {
// 这里self可以访问“父类”B的成员了
}
}
B::do_all_steps(&mut self)就没法在“父类”B中统一实现了,因为B<T>在B<B1>具象化之前,还不知道哪来的do_step2,因此要在impl B<B1>中实现,每个不同的具像化的子类都得单独实现相同的do_all_steps!你能接受不?B<B1>、B<B2>...重复拷贝一遍各自的do_all_steps!本文基于专业探讨,还是要寻找一下编写通用的do_all_steps方法的,有没有?当然是有的,前提是,你得把do_step1_common,do_step3_common也得trait化,然后在用一个trait组合限定搞定,如下:trait DoStep1 {
fn do_step1_common(&mut self);
}
trait DoStep3 {
fn do_step2_common(&mut self);
}
// 因为B<T>是泛型,只需为泛型编码实现一次DoStep1、DoStep3就行
impl<T> DoStep1 for B<T> { ... }
impl<T> DoStep3 for B<T> { ... }
// 最后,实现通用的do_all_steps,还得靠泛型。
// 此时,B<B1>已经满足T,会为其实现下面的函数
// 可以这样读:为所有实现了DoStep1/DoStep2/DoStep3特质的类型T实现do_all_steps
impl<T> T
where
T: DoStep1 + DoStep2 + DoStep3
{
pub fn do_all_steps(&mut self) {
self.do_step1_common();
self.do_step2_maybe_different();
self.do_step3_common();
}
}
do_step1和do_step3函数永远指向了同一个实现,而Rust靠泛型应该是指向了3个不同的实现?不知道编译期有没有优化,盲猜应该有。可以说语法如此,Rust只能做到如此了。与面向对象的模板方法相比,最后一点小瑕疵,就是要多定义DoStep1、DoStep2 2个trait,并用一个T: DoStep1 + DoStep2 + DoStep3通用类型包含同样实现了DoStep1 + DoStep2 + DoStep3的B<T>,进而代表它。可我们想仅仅为B<T>类型实现,其他类型也不太可能这样实现了,一个T则把范围不必要地扩大了。要是能按照我们想要的,就仅为B<T>且实现了DoStep2的B<T>来实现do_all_steps,就完美了。要做到此种程度,必须能对自身Self进行限定,如下:/// 可以这样读:为所有自身实现了DoStep2的B<T>实现do_all_steps
impl<T> B<T>
where
Self: DoStep2
{
pub fn do_all_steps(&mut self) {
self.do_step1_common();
self.do_step2_maybe_different();
self.do_step3_common();
}
}
B<T>已经有do_step1_common/do_step3_common的实现了,Rust最新的稳定版就支持这样写!原文作者:Rust语言中文社区;如有侵权,请联系管理员删除。