如果我有此代码:
trait Trait {
fn f(&self) -> i32 where Self: Sized;
fn g(&self) -> i32;
}
fn object_safety_dynamic(x: &Trait) {
x.f(); // error
x.g(); // works
}
该where
条款实际上是做什么的?
天真,我在想where Self: Sized;
使然一些关于类型实现Trait
,像“如果实现Trait
类型A
的类型A
必须调整,即,它可以i32
但不是[i32]
。
但是,这样的约束宁可去trait Trait: Sized
(如果我错了,请纠正我)?
现在我注意到where Self: Sized;
实际上确定了是否可以打电话f
或g
从内打电话object_safety_dynamic
。
我的问题:
幕后发生了什么?
我实际上是在告诉编译器什么(用简单的英语)where Self: Sized;
,g()
但f()
没有奏效?
特别是:由于&self
无论如何都是参考,因此在各种(大小或未大小)类型之间f
以及g
对于各种(大小或未大小)类型,已编译的差异存在。_vtable_f_or_g(*self) -> i32
不管where
类型是否大小,它是否总会归结为类似的东西?
为什么我要为u8
和都实施Trait [u8]
。编译器是否应该实际上阻止我实施f()
for [u8]
,而不是在调用站点上抛出错误?
where子句实际上是做什么的?
天真的,我在想Self:Sized; 规定了有关实现Trait的类型的某些内容,例如,“如果您为Type A实现Trait,则您的Type A必须是大小,即可以是i32,但不能为[i32]。
但是,这样的约束更适合作为特征:特质
这是对的。
但是,在这种情况下,界限仅适用于该功能。where
仅在呼叫站点上检查功能范围。
幕后发生了什么?
关于rust的语法有一个令人困惑的地方,那就是Trait
可以引用
Trait
; 要么Trait
实际上是一种类型,而不是对象。Sized
是一个特征,任何类型的大小T
都Sized
可以通过来作为常量std::mem::size_of::<T>()
。此类没有大小的类型是str
和[u8]
,其内容的大小没有固定的大小。
该类型Trait
也没有大小。直观地讲,这是因为Trait
作为一个类型,它包含实现trait的类型的所有值Trait
,它们的大小可能有所不同。这意味着您永远不会拥有类型的值Trait
-您只能通过“胖指针”(例如&Trait
orBox<Trait>
等等)引用一个值。它们的大小为2个指针-一个用于vtable,一个用于数据。大致如下所示:
struct &Trait {
pub data: *mut (),
pub vtable: *mut (),
}
系统会自动提供以下形式的展示:
impl Trait /* the trait */ for Trait /* the type */ {
fn f(&self) -> i32 where Self: Sized { .. }
fn g(&self) -> i32 {
/* vtable magic: something like (self.vtable.g)(self.data) */
}
}
我实际上是通过Self:Sized告诉编译器的是什么?这使g()有效,但f()不起作用?
请注意,由于正如我所提到Trait
的那样Sized
,Self: Sized
不满足边界要求,因此f
不能在where调用该函数Self == Trait
。
特别是:由于&self仍然是引用,因此对于各种(大小或未大小大小)类型,f和g之间存在已编译的差异。不管类型在何处或是否在大小上,它是否总是会归结为_vtable_f_or_g(* self)-> i32之类的东西?
该类型Trait
是始终未上浆。强制类型无关紧要Trait
。Sized
使用变量调用函数的方式是直接使用它:
fn generic<T: Trait + Sized>(x: &T) { // the `Sized` bound is implicit, added here for clarity
x.f(); // compiles just fine
x.g();
}
为什么我要为u8和[u8]都实现Trait。编译器是否应该阻止我为[u8]实现f(),而不是在调用站点上抛出错误?
因为特征不受Self: Sized
-函数f
的限制。因此,没有什么可以阻止您实现该功能-只是永远无法满足该功能的界限,因此您永远无法调用它。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句