如果我有这段代码:
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
类型{{1}必须是大小的,即它可以是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
实施特征。难道编译器真的不能阻止我为[u8]
实施f()
,而不是在呼叫站点抛出错误吗?
答案 0 :(得分:3)
fn f(&self) -> i32 where Self: Sized;
这表示f
仅针对同时实现Sized
的类型定义。未归档的类型仍可以实现Trait
,但f
将无法使用。
在object_safety_dynamic
内,调用x.f()
实际上正在执行:(*x).f()
。虽然x
的大小是因为它是指针,但*x
可能不是因为它可能是 Trait
的任何实现。但是函数内部的代码必须适用于任何有效参数,因此不允许在那里调用x.f()
。
答案 1 :(得分:3)
where子句实际上做了什么?
天真地,我在想自我:大小;指示实现Trait的类型,例如'如果你为A类实现Trait,你的类型A必须是大小的,即它可以是i32而不是[i32]。
然而,这样的约束宁愿作为特质Trait:Sized
这是正确的。
但是,在这种情况下,绑定仅适用于该函数。仅在呼叫站点检查函数的where
边界。
幕后会发生什么?
关于rust的语法有一个令人困惑的地方,Trait
可以指代
Trait
;或Trait
,实际上是一种类型,而不是一种对象。 Sized
是一种特征,T
的任何类型Sized
都可以将其大小视为std::mem::size_of::<T>()
的常量。这些未调整大小的类型为str
和[u8]
,其内容的大小不固定。
类型Trait
也是未标注的。直观地说,这是因为作为类型的Trait
由实现特征Trait
的所有类型值组成,其可能具有不同的大小。这意味着你永远不会有Trait
类型的值 - 你只能通过&#34;胖指针&#34;来引用一个值。例如&Trait
或Box<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
,因此无法在{{1}处调用函数f
}}
特别是:因为&amp; self无论如何都是参考,对于各种(大小或未大小)类型,f和g之间存在编译差异。它不会总是归结为像_vtable_f_or_g(* self) - &gt;这样的东西。 i32,无论在何处或类型是否大小?
类型Self == Trait
总是未确定。哪种类型被强制转移到Trait
并不重要。使用Trait
变量调用函数的方法是直接使用它:
Sized
为什么我可以为u8和[u8]实现Trait。难道编译器实际上不能阻止我为[u8]实现f(),而不是在调用站点抛出错误吗?
因为特征不受fn generic<T: Trait + Sized>(x: &T) { // the `Sized` bound is implicit, added here for clarity
x.f(); // compiles just fine
x.g();
}
限制 - 函数Self: Sized
是。{所以没有什么可以阻止你实现这个功能 - 只是因为函数的界限永远不会被满足,所以你永远不能称之为。