创建盒装特征背后的机制是如何工作的? [英] How does the mechanism behind the creation of boxed traits work?
问题描述
我无法理解盒装特征的价值是如何产生的.考虑以下代码:
trait Fooer {fn foo(&self);}i32 的 impl Fooer {fn foo(&self) { println!("Fooer on i32!");}}fn 主(){让 a = Box::new(32);//工作,创建一个 Box<i32>让 b = Box::::new(32);//工作,创建一个 Box<i32>让 c = Box::::new(32);//不起作用让 d: Box= 盒子::新(32);//有效,创建一个 Box让 e: Box= Box::<i32>::new(32);//有效,创建一个 Box}
显然,变体 a 和 b 可以正常工作.但是,变体 c 没有,可能是因为 new
函数只接受相同类型的值,而 Fooer != i32
并非如此.变体 d 和 e 工作,这让我怀疑正在执行某种从 Box
到 Box
的自动转换.
所以我的问题是:
- 这里是否发生了某种转换?
- 如果是这样,它背后的机制是什么?它是如何工作的?(我也对底层细节感兴趣,即事物在幕后如何表示)
- 有没有办法直接从
i32
创建一个Box
?如果没有:为什么不呢?
然而,变体 c 没有,可能是因为
new
函数只接受相同类型的值,而Fooer != i32
并非如此.
不,这是因为没有Box
没有new
函数.在文档中:
impl;方框
pub fn new(x: T) ->方框
Box
上的大多数方法都允许 T: ?Sized
,但 new
是在 impl
中定义的> 没有T: ?Sized
绑定.意味着你只能调用Box::<T>::new
当 T
是已知大小的类型时.dyn Fooer
未调整大小,因此根本没有要调用的 new
函数.
事实上,这个函数不能存在于今天的 Rust 中.Box
需要知道T
的具体类型,以便分配合适大小和对齐方式的内存.因此,在将它发送到 Box::new
之前,您不能擦除 T
.(可以想象未来的语言扩展 可能允许函数接受未定义大小的参数;但是,尚不清楚即使 unsized_locals
是否真的会启用 Box
接受未定义大小的 T
>.)
暂时,像 dyn Fooer
这样的 unsized 类型只能存在于胖指针"之后,即指向对象的指针和指向该对象的 Fooer
的实现.你如何得到一个胖指针?你从一个细指针开始并强制它.这就是这两行发生的事情:
let d: Box= 盒子::新(32);//有效,创建一个 Box让 e: Box= Box::<i32>::new(32);//有效,创建一个 Box
Box::new
返回一个 Box
,然后是 强制 到 Box
.您可以将其视为一种转换,但是 Box
没有改变;编译器所做的只是在上面贴一个额外的指针,然后忘记它的原始类型.rodrigo 的回答更详细地介绍了这种强制转换的语言级机制.
希望所有这些都能解释为什么答案
<块引用>有没有办法直接从 i32
创建一个 Box
?
是否":i32
必须在 之前被装箱,您可以删除它的类型.这和你不能写 let x: Fooer = 10i32
的原因是一样的.
相关
I'm having trouble understanding how values of boxed traits come into existence. Consider the following code:
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>
}
Obviously, variant a and b work, trivially. 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
. Variant d and e work, which lets me suspect that some kind of automatic conversion from Box<i32>
to Box<dyn Fooer>
is being performed.
So my questions are:
- Does some kind of conversion happen here?
- If so, what the mechanism behind it and how does it work? (I'm also interested in the low level details, i.e. how stuff is represented under the hood)
- Is there a way to create a
Box<dyn Fooer>
directly from ani32
? If not: why not?
However, variant c does not, probably because the
new
function takes only values of the same type which is not the case sinceFooer != i32
.
No, it's because there is no new
function for Box<dyn Fooer>
. In the documentation:
impl<T> Box<T>
pub fn new(x: T) -> Box<T>
Most methods on Box<T>
allow T: ?Sized
, but new
is defined in an impl
without a T: ?Sized
bound. That means you can only call Box::<T>::new
when T
is a type with a known size. dyn Fooer
is unsized, so there simply isn't a new
function to call.
In fact, that function can't exist in today's Rust. Box<T>::new
needs to know the concrete type T
so that it can allocate memory of the right size and alignment. Therefore, you can't erase T
before you send it to Box::new
. (It's conceivable that future language extensions may allow functions to accept unsized parameters; however, it's unclear whether even unsized_locals
would actually enable Box<T>::new
to accept unsized T
.)
For the time being, unsized types like dyn Fooer
can only exist behind a "fat pointer", that is, a pointer to the object and a pointer to the implementation of Fooer
for that object. How do you get a fat pointer? You start with a thin pointer and coerce it. That's what's happening in these two lines:
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>
, which is then 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. rodrigo's answer goes into more detail about the language-level mechanics of this coercion.
Hopefully all of this goes to explain why the answer to
Is there a way to create a
Box<Fooer>
directly from ani32
?
is "no": the i32
has to be boxed before you can erase its type. It's the same reason you can't write let x: Fooer = 10i32
.
Related
- Why can't I write a function with the same type as Box::new?
- Are polymorphic variables allowed?
- How do you actually use dynamically sized types in Rust?
- Why is `let ref a: Trait = Struct` forbidden?
这篇关于创建盒装特征背后的机制是如何工作的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!