盒装特征创建背后的机制如何运作?
How does the mechanism behind the creation of boxed traits work?
我无法理解盒装特征的值是如何产生的。考虑以下代码:
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::<dyn Fooer>::new(32); // doesn't work
let d: Box<dyn Fooer> = Box::new(32); // works, creates a Box<Fooer>
let e: Box<dyn Fooer> = Box::<i32>::new(32); // works, creates a Box<Fooer>
}
显然,变体 a 和 b 可以正常工作。但是,变体 c 没有,可能是因为 new
函数仅采用相同类型的值,而自 Fooer != i32
以来情况并非如此。变体 d 和 e 工作,这让我怀疑正在执行某种从 Box<i32>
到 Box<dyn Fooer>
的自动转换。
所以我的问题是:
- 这里是否发生某种转换?
- 如果是这样,它背后的机制是什么,它是如何工作的? (我也对底层细节感兴趣,即底层是如何表示的)
- 有没有办法直接从
i32
创建 Box<dyn Fooer>
?如果不是:为什么不呢?
我将尝试解释您的代码中发生了哪些转换(强制转换)。
有一个名为 Unsize
的标记特征,在其他特征之间:
Unsize is implemented for:
T
is Unsize<Trait>
when T: Trait
.
- [...]
据我所知,这个特征不直接用于强制转换。相反,使用 CoerceUnsized
。这个trait在很多情况下都实现了,其中有一些是意料之中的,比如:
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>
.
关于你的最后一个问题:
Is there a way to create a Box<Fooer>
directly from an i32
? If not: why not?
据我所知没有。问题是 Box::new(T)
需要一个大小的值,因为传递的值被移动到框中,而未大小的值不能移动。
在我看来,最简单的方法就是简单地写:
let c = Box::new(42) as Box<Fooer>;
也就是说,您创建了一个正确类型的 Box
,然后强制转换为未调整大小的类型(注意它看起来与您的 d
示例非常相似)。
However, variant c does not, probably because the new
function takes only values of the same type which is not the case since Fooer != i32
.
没有,因为没有Box<dyn Fooer>
的new
功能。在 documentation:
impl<T> Box<T>
pub fn new(x: T) -> Box<T>
Box<T>
上的大多数方法允许 T: ?Sized
,但是 new
是在 impl
中定义的 T: ?Sized
绑定。当 T
是已知大小的类型时, 只能调用 Box::<T>::new
。 dyn Fooer
未调整大小,因此根本没有要调用的 new
函数。
事实上,那个函数 不能 存在于今天的 Rust 中。 Box<T>::new
需要知道具体类型 T
以便它可以分配正确大小和对齐的内存。因此,在发送到Box::new
之前,你不能擦除T
。 (可以想象 future language extensions 可能允许函数接受未调整大小的参数;但是,尚不清楚甚至 unsized_locals
是否真的会启用 Box<T>::new
接受未调整大小的 T
。)
目前,像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
returns a Box<i32>
,然后 coerced to Box<Fooer>
. You could consider this a conversion, but the Box
isn't changed; all the compiler does is stick an extra pointer on it and forget its original type. 详细介绍了这种强制转换的语言级机制。
希望所有这些都能解释为什么
Is there a way to create a Box<Fooer>
directly from an i32
?
是“否”:i32
必须先装箱 ,然后 才能擦除其类型。这和你不能写 let x: Fooer = 10i32
.
的原因是一样的
相关
- How do you actually use dynamically sized types in Rust?
- Why is `let ref a: Trait = Struct` forbidden?
我无法理解盒装特征的值是如何产生的。考虑以下代码:
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::<dyn Fooer>::new(32); // doesn't work
let d: Box<dyn Fooer> = Box::new(32); // works, creates a Box<Fooer>
let e: Box<dyn Fooer> = Box::<i32>::new(32); // works, creates a Box<Fooer>
}
显然,变体 a 和 b 可以正常工作。但是,变体 c 没有,可能是因为 new
函数仅采用相同类型的值,而自 Fooer != i32
以来情况并非如此。变体 d 和 e 工作,这让我怀疑正在执行某种从 Box<i32>
到 Box<dyn Fooer>
的自动转换。
所以我的问题是:
- 这里是否发生某种转换?
- 如果是这样,它背后的机制是什么,它是如何工作的? (我也对底层细节感兴趣,即底层是如何表示的)
- 有没有办法直接从
i32
创建Box<dyn Fooer>
?如果不是:为什么不呢?
我将尝试解释您的代码中发生了哪些转换(强制转换)。
有一个名为 Unsize
的标记特征,在其他特征之间:
Unsize is implemented for:
T
isUnsize<Trait>
whenT: Trait
.- [...]
据我所知,这个特征不直接用于强制转换。相反,使用 CoerceUnsized
。这个trait在很多情况下都实现了,其中有一些是意料之中的,比如:
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>
.
关于你的最后一个问题:
Is there a way to create a
Box<Fooer>
directly from ani32
? If not: why not?
据我所知没有。问题是 Box::new(T)
需要一个大小的值,因为传递的值被移动到框中,而未大小的值不能移动。
在我看来,最简单的方法就是简单地写:
let c = Box::new(42) as Box<Fooer>;
也就是说,您创建了一个正确类型的 Box
,然后强制转换为未调整大小的类型(注意它看起来与您的 d
示例非常相似)。
However, variant c does not, probably because the
new
function takes only values of the same type which is not the case sinceFooer != i32
.
没有,因为没有Box<dyn Fooer>
的new
功能。在 documentation:
impl<T> Box<T>
pub fn new(x: T) -> Box<T>
Box<T>
上的大多数方法允许 T: ?Sized
,但是 new
是在 impl
中定义的 T: ?Sized
绑定。当 T
是已知大小的类型时,Box::<T>::new
。 dyn Fooer
未调整大小,因此根本没有要调用的 new
函数。
事实上,那个函数 不能 存在于今天的 Rust 中。 Box<T>::new
需要知道具体类型 T
以便它可以分配正确大小和对齐的内存。因此,在发送到Box::new
之前,你不能擦除T
。 (可以想象 future language extensions 可能允许函数接受未调整大小的参数;但是,尚不清楚甚至 unsized_locals
是否真的会启用 Box<T>::new
接受未调整大小的 T
。)
目前,像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
returns a Box<i32>
,然后 coerced to Box<Fooer>
. You could consider this a conversion, but the Box
isn't changed; all the compiler does is stick an extra pointer on it and forget its original type.
希望所有这些都能解释为什么
Is there a way to create a
Box<Fooer>
directly from ani32
?
是“否”:i32
必须先装箱 ,然后 才能擦除其类型。这和你不能写 let x: Fooer = 10i32
.
相关
- How do you actually use dynamically sized types in Rust?
- Why is `let ref a: Trait = Struct` forbidden?