我无法理解盒装特征的价值是如何形成的。请考虑以下代码:
trait Fooer {
fn foo(&self);
}
impl Fooer for i32 {
fn foo(&self) { println!("Fooer on i32!"); }
}
fn main() {
let a = Box::new(32); // works, creates a Box<i32>
let b = Box::<i32>::new(32); // works, creates a Box<i32>
let c = Box::<Fooer>::new(32); // doesn't work
let d: Box<Fooer> = Box::new(32); // works, creates a Box<Fooer>
let e: Box<Fooer> = Box::<i32>::new(32); // works, creates a Box<Fooer>
}
显然,变体a和b是微不足道的。但是,变量c没有,可能是因为new
函数只采用相同类型的值,而自Fooer != i32
以来并非如此。变体d和e工作,这让我怀疑正在进行从Box<i32>
到Box<Fooer>
的某种自动转换。
所以我的问题是:
Box<Fooer>
创建i32
?如果没有:为什么不呢?但是,变量c没有,可能是因为
new
函数只采用相同类型的值,而自Fooer != i32
以来并非如此。
不,这是因为new
没有Box<dyn Fooer>
功能。在documentation:
impl<T> Box<T>
pub fn new(x: T) -> Box<T>
Box<T>
上的大多数方法都允许使用T: ?Sized
,但new
是在没有impl
约束的T: ?Sized
中定义的。 means你只能在Box::<T>::new
是一个已知大小的类型时调用T
。 dyn Fooer
未经过调整,因此根本没有new
方法可以调用。
实际上,那种方法不可能存在。为了装箱,你需要知道它的大小。为了将它传递给函数,您需要知道它的大小。为了甚至包含一个包含某些内容的变量,它必须具有一个大小。像dyn Fooer
这样的非类型类型只能存在于“胖指针”后面,即指向该对象的指针和指向该对象的Fooer
实现的指针。
你怎么得到胖指针?你从一个细指针开始并强制它。这就是这两行中发生的事情:
let d: Box<Fooer> = Box::new(32); // works, creates a Box<Fooer>
let e: Box<Fooer> = Box::<i32>::new(32); // works, creates a Box<Fooer>
Box::new
返回一个Box<i32>
,然后是coerced到Box<Fooer>
。你可以认为这是一个转换,但Box
没有改变;所有编译器都会在其上粘贴一个额外的指针并忘记它的原始类型。 rodrigo's answer详细介绍了这种胁迫的语言层面的机制。
希望所有这一切都可以解释为什么答案
有没有办法直接从
Box<Fooer>
创建i32
?
是“不”:i32
必须先装箱才能擦掉它的类型。这是你不能写let x: Fooer = 10i32
的原因。
我将尝试解释代码中发生的转换(强制)。
有一个名为Unsize
的标记特征,在其他之间:
Unsize实现为:
- 当
T
时,Unsize<Trait>
是T: Trait
。- [...]
这种特性AFAIK不直接用于强制。相反,使用CoerceUnsized
。这个特性在很多情况下实现,其中一些是非常期望的,例如:
impl<'a, 'b, T, U> CoerceUnsized<&'a U> for &'b T
where
'b: 'a,
T: Unsize<U> + ?Sized,
U: ?Sized
那用来强迫&i32
进入&Fooer
。
影响代码的有趣的,不那么明显的特性实现是:
impl<T, U> CoerceUnsized<Box<U>> for Box<T>
where
T: Unsize<U> + ?Sized,
U: ?Sized
这与Unsize
标记的定义一起可以解释为:如果U
是特征而T
实现U
,那么Box<T>
可以被强制进入Box<U>
。
关于你的上一个问题:
有没有办法直接从
Box<Fooer>
创建i32
?如果没有:为什么不呢?
从来没听说过。问题是Box::new(T)
需要一个大小的值,因为传递的值被移动到框中,并且无法移动未大小的值。
在我看来,最简单的方法是简单地写:
let c = Box::new(42) as Box<Fooer>;
也就是说,你创建一个正确类型的Box
然后强制到未经过尺寸化的那个(注意它看起来非常类似于你的d
例子)。