rust - 为什么 `Self` 在编译时需要一个常量大小来调用自由函数,而等效的特征函数却不需要?

标签 rust traits

这两个函数,一个 trait 和一个 free,看起来很相似,但是允许调用其中一个(trait 函数)而调用另一个则不允许:

trait A {
    fn foo(&self) {
        bar(self);    // 1. Error: `Self` does not have a constant size known at compile-time
        A::bar(self); // 2. This works
    }

    fn bar(&self) {}
}

fn bar(_a: &A) {}

( Playground link for above )

我本以为在这两种情况下,我都是通过一个在编译时已知大小 的指针进行访问,那么这种行为有什么区别和解释?

(Rust 1.19 稳定版)

最佳答案

因为两者想要的东西完全不同。

A::fooself的类型是&Self不是 &A。也就是说,它是指向实现类型的指针,而不是指向特征对象的指针。调用 A::bar 很好,因为它只是传递所述指针,不需要额外的工作。

调用 ::bar 是一个完全不同的海洋生物 cooking 容器。

问题归结为编译器如何表示事物。让我们首先考虑 SelfSized 类型的情况,例如 i32。这意味着 self 是一个 &i32。回想一下,&A 是一个特征对象,因此有效地具有以下布局:

struct ATraitObject {
    ptr: *const (),
    vtable: *const AVtable,
}

ATraitObject.ptr 需要指向实际值本身,ATraitObject.vtable 需要指向该类型的特征实现。编译器可以用 ptr 作为现有的 self 指针来填充它,并且 vtable 填充指向 impl A 的任何地方的指针for i32 vtable 存在于内存中。这样,它就可以调用 ::bar

现在考虑 Self 不是 Sized 的情况,比如 str。这意味着 self 是一个“胖”指针,包含两个指针的信息值(value):指向基础数据的指针 字符串的长度。当编译器创建ATraitObject时,它可以将ptr设置为self.as_ptr(),并且可以设置vtable...但是它没有地方可以存储字符串的长度!

所有非Sized 类型都有这个问题,因为它们的指针中都有额外的信息。顺便说一句,这包括特征对象,如&A,这意味着你也不能将特征对象变成另一个特征对象,因为你现在需要两个虚表。

这就是问题所在:编译器根本没有办法&Self where Self: !Sized 转换为 &Aself 中包含的信息太多,而 &A 中没有足够的空间来存储它。


要获得要编译的方法,请将 where Self: Sized 子句添加到方法定义中。这向编译器保证该方法永远不会在非 Sized 类型上调用,因此可以自由地假设从 &Self&A 的转换> 总是可能的。

关于rust - 为什么 `Self` 在编译时需要一个常量大小来调用自由函数,而等效的特征函数却不需要?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45996828/

相关文章:

rust - 错误 : reached the recursion limit while instantiating `func::<[closure]>`

rust - 是否有任何特征可以指定数字功能?

c++ - C+ +'s concepts and Rust' 的特质有什么异同?

iterator - 使用修改后的 `Chars` 迭代器时生命周期参数的数量错误

php - PHP 中 Trait 和抽象类的区别

rust - 将可迭代传递给函数并在 Rust 中迭代两次

rust - 实例化 std::vec::IntoIter<i32> 时达到类型长度限制

Scala 匿名混入?

rust - 如何访问 Actix-web 中 Future 中的 HttpRequest 数据?

closures - 无法将闭包作为参数传递