Pao*_*lla 54
当您有指向特征的指针时,您有特征对象.
Box
,Arc
,Rc
和基准&
都在其核心的指针.在定义"特征对象"方面,它们以相同的方式工作.
"特质对象"是Rust对动态调度的看法.这是一个我希望有助于显示特征对象的示例:
// define an example struct, make it printable
#[derive(Debug)]
struct Foo;
// an example trait
trait Bar {
fn baz(&self);
}
// implement the trait for Foo
impl Bar for Foo {
fn baz(&self) { println!("{:?}", self) }
}
// This is a generic function that takes any T that implements trait Bar.
// It must resolve to a specific concrete T at compile time.
// The compiler creates a different version of this function
// for each concrete type used to call it so &T here is NOT
// a trait object (as T will represent a known, sized type
// after compilation)
fn static_dispatch<T>(t: &T) where T:Bar {
t.baz(); // we can do this because t implements Bar
}
// This function takes a pointer to a something that implements trait Bar
// (it'll know what it is only at runtime). &dyn Bar is a trait object.
// There's only one version of this function at runtime, so this
// reduces the size of the compiled program if the function
// is called with several different types vs using static_dispatch.
// However performance is slightly lower, as the &dyn Bar that
// dynamic_dispatch receives is a pointer to the object +
// a vtable with all the Bar methods that the object implements.
// Calling baz() on t means having to look it up in this vtable.
fn dynamic_dispatch(t: &dyn Bar) {
// ----------------^
// this is the trait object! It would also work with Box<dyn Bar> or
// Rc<dyn Bar> or Arc<dyn Bar>
//
t.baz(); // we can do this because t implements Bar
}
fn main() {
let foo = Foo;
static_dispatch(&foo);
dynamic_dispatch(&foo);
}
Run Code Online (Sandbox Code Playgroud)
为了进一步参考,Rust书中有一个很好的Trait Objects章节
Trait 对象是动态调度的 Rust 实现。动态分派允许在运行时选择多态操作(特征方法)的一种特定实现。动态调度允许非常灵活的架构,因为我们可以在运行时交换函数实现。然而,与动态调度相关的运行时成本很小。
\n保存特征对象的变量/参数是胖指针,由以下组件组成:
\nstruct Point {\n x: i64,\n y: i64,\n z: i64,\n}\n\ntrait Print {\n fn print(&self);\n}\n\n// dyn Print is actually a type and we can implement methods on it\nimpl dyn Print + 'static {\n fn print_traitobject(&self) {\n println!("from trait object");\n }\n}\n\nimpl Print for Point {\n fn print(&self) {\n println!("x: {}, y: {}, z: {}", self.x, self.y, self.z);\n }\n}\n\n// static dispatch (compile time): compiler must know specific versions\n// at compile time generates a version for each type\n\n// compiler will use monomorphization to create different versions of the function\n// for each type. However, because they can be inlined, it generally has a faster runtime\n// compared to dynamic dispatch\nfn static_dispatch<T: Print>(point: &T) {\n point.print();\n}\n\n// dynamic dispatch (run time): compiler doesn't need to know specific versions\n// at compile time because it will use a pointer to the data and the vtable.\n// The vtable contains pointers to all the different different function implementations.\n// Because it has to do lookups at runtime it is generally slower compared to static dispatch\n\n// point_trait_obj is a trait object\nfn dynamic_dispatch(point_trait_obj: &(dyn Print + 'static)) {\n point_trait_obj.print();\n point_trait_obj.print_traitobject();\n}\n\nfn main() {\n let point = Point { x: 1, y: 2, z: 3 };\n\n // On the next line the compiler knows that the generic type T is Point\n static_dispatch(&point);\n\n // This function takes any obj which implements Print trait\n // We could, at runtime, change the specfic type as long as it implements the Print trait\n dynamic_dispatch(&point);\n}\n
Run Code Online (Sandbox Code Playgroud)\n
简短回答:您只能将对象安全的特征变成特征对象。
对象安全特征:不解析为具体实现类型的特征。在实践中,有两个规则控制着一个 trait 是否是对象安全的。
任何满足这两条规则的特征都可以用作特征对象。
对象安全的特征示例可以用作特征对象:
trait Draw {
fn draw(&self);
}
Run Code Online (Sandbox Code Playgroud)
不能用作特征对象的特征示例:
trait Draw {
fn draw(&self) -> Self;
}
Run Code Online (Sandbox Code Playgroud)
详细解释:https : //doc.rust-lang.org/book/second-edition/ch17-02-trait-objects.html
归档时间: |
|
查看次数: |
5858 次 |
最近记录: |