当以"Self:Sized"为界时,为什么不能调用特征对象上的函数? [英] Why can a function on a trait object not be called when bounded with `Self: Sized`?

查看:116
本文介绍了当以"Self:Sized"为界时,为什么不能调用特征对象上的函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码:

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();
}

操场

哪个会导致此错误:

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

为什么这不可能?当我删除Self: Sized绑定时,它可以工作,但是我不能使用使函数对调用者更舒服的泛型.

Why is this not possible? It works when I remove the Self: Sized bound, but then I can't use generics which make the function more comfortable for the caller.

这不是特质内的通用方法为什么要求特质对象具有一定大小的原因?无法从特征对象调用baz.我不是在问为什么需要这个界限.这已经已经进行了讨论.

This is not a duplicate of Why does a generic method inside a trait require trait object to be sized? which asks why you can't call baz from a trait object. I'm not asking why the bound is required; this has already been discussed.

推荐答案

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

Because Rust's generics system works through monomorphization.

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

In Java, for example, type parameters in a generic function turn into variables of type Object, and are casted as necessary. Generics in languages like this simply serves as a tool to help verify the correctness of types within code.

Rust和C ++等语言对泛型使用单态化.对于类型参数的每个组合,将调用通用函数,并生成专用机器码,该机器码将使用这些类型参数的组合运行该函数.该函数是 monomorphized .这样可以将数据存储在适当的位置,消除了转换成本,并允许通用代码在该类型参数上调用静态"函数.

Languages such as Rust and C++ use monomorphization for generics. For each combination of type parameters a generic function is invoked with, specialized machine code is generated which runs that function with those combinations of type parameters. The function is monomorphized. This allows data to be stored in place, eliminates the cost of casting, and allows the generic code to call "static" functions on that type paramameter.

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

So why can't you do that on a trait object?

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

Trait objects in many languages, including Rust, are implemented using a vtable. When you have some type of pointer to a trait object (raw, reference, Box, reference counter, etc.), it contains two pointers: the pointer to the data, and a pointer to a vtable entry. The vtable entry is a collection of function pointers, stored in an immutable memory region, which point to the implementation of that trait's methods. Thus, when you call a method on a trait object, it looks up the function pointer of the implementation in the vtable, and then makes an indirect jump to that pointer.

不幸的是,如果在编译时Rust编译器不知道实现该功能的代码,则Rust编译器无法使函数单态化(在特征对象上调用方法时就是这种情况).因此,您不能在特征对象上调用泛型函数(当然,泛型超过类型).

Unfortunately, the Rust compiler cannot monomorphize functions, if it does not know at compile time the code that implements the function, which is the case when you call a method on a trait object. For that reason, you cannot call a generic function (well, generic over types) on a trait object.

-编辑-

这听起来像是您在问为什么需要: Sized限制.

It sounds like you're asking why the : Sized restriction is necessary.

: Sized使其无法用作特征对象.我想可能有两种选择. Rust可以隐式地使具有泛型函数的任何特征都不安全. Rust还可以隐式阻止在特征对象上调用泛型函数.

: Sized makes it so that the trait cannot be used as a trait object. I suppose there could be a couple of alternatives. Rust could implicitly make any trait with generic functions not object safe. Rust could also implicitly prevent generic functions from being called on trait objects.

但是,Rust试图明确地说明编译器在做什么,这些隐式方法会违背这种做法.无论如何,对于初学者来说,尝试在trait对象上调用泛型函数并且编译失败会不会引起混淆?

However, Rust tries to be explicit with what the compiler is doing, which these implicit approaches would go against. Wouldn't it be confusing, anyways, for a beginner to try and call a generic function on a trait object and have it fail to compile?

相反,Rust让您明确地使整个特征不安全.

Instead, Rust lets you explicitly make the entire trait not object safe

trait Foo: Sized {

或者明确地使某些功能仅在静态分派中可用

Or explicitly make certain functions only available with static dispatch

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

这篇关于当以"Self:Sized"为界时,为什么不能调用特征对象上的函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆