gpt4 book ai didi

rust - 为什么在以 `Self: Sized` 为界时不能调用特征对象上的函数?

转载 作者:行者123 更新时间:2023-11-29 08:00:16 24 4
gpt4 key购买 nike

我有以下代码:

trait Bar {
fn baz(&self, arg: impl AsRef<str>)
where
Self: Sized;
}

struct Foo;

impl Bar for Foo {
fn baz(&self, arg: impl AsRef<str>) {}
}

fn main() {
let boxed: Box<dyn Bar> = Box::new(Foo);
boxed.baz();
}

playground

导致此错误的原因:

error: the `baz` method cannot be invoked on a trait object
--> src/main.rs:15:11
|
15 | boxed.baz();
| ^^^

为什么这不可能?当我删除 Self: Sized 绑定(bind)时它会起作用,但是我不能使用泛型来让调用者更舒适地使用函数。

这不是 Why does a generic method inside a trait require trait object to be sized? 的副本这会问为什么不能从特征对象调用 baz。我不是在问为什么需要绑定(bind);这有 already been discussed .

最佳答案

因为 Rust 的泛型系统通过单态化工作。

例如,在 Java 中,泛型函数中的类型参数会转换为 Object 类型的变量,并根据需要进行强制转换。像这样的语言中的泛型只是作为一种工具来帮助验证代码中类型的正确性。

Rust 和 C++ 等语言对泛型使用单态化。对于调用泛型函数的每种类型参数组合,都会生成专门的机器代码,该代码使用这些类型参数组合运行该函数。该函数是单态的。这允许数据存储在适当的位置,消除转换成本,并允许通用代码调用该类型参数的“静态”函数。

那么为什么你不能在特征对象上这样做呢?

许多语言(包括 Rust)中的 Trait 对象都是使用 vtable 实现的。当您有某种类型的指向特征对象(原始、引用、Box、引用计数器等)的指针时,它包含两个指针:指向数据的指针和指向 vtable 条目的指针. vtable 条目是函数指针的集合,存储在不可变的内存区域中,指向该特征方法的实现。因此,当您在特征对象上调用方法时,它会在 vtable 中查找实现的函数指针,然后间接跳转到该指针。

不幸的是,Rust 编译器无法单态化函数,如果它在编译时不知道实现该函数的代码,当您在 trait 对象上调用方法时就是这种情况。因此,您不能在 trait 对象上调用泛型函数(嗯,泛型类型)。

-编辑-

听起来你在问为什么 : Sized限制是必要的。

: Sized使得特征不能用作特征对象。我想可能有几种选择。 Rust 可以隐式地使具有泛型函数的任何特征都不是对象安全的。 Rust 还可以隐式地阻止泛型函数在 trait 对象上被调用。

然而,Rust 试图明确编译器正在做什么,而这些隐式方法会违背这一点。不管怎样,对于初学者来说,尝试在特征对象上调用泛型函数却编译失败,这不会让人感到困惑吗?

相反,Rust 让您明确地使整个特征不是对象安全的

trait Foo: Sized {

或者显式使某些功能仅在静态调度时可用

fn foo<T>() where Self: Sized {

关于rust - 为什么在以 `Self: Sized` 为界时不能调用特征对象上的函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51822118/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com