特质中的'where'子句有什么作用?

问题描述 投票:2回答:2

如果我有这个代码:

trait Trait {
    fn f(&self) -> i32 where Self: Sized;

    fn g(&self) -> i32;
}


fn object_safety_dynamic(x: &Trait) {
    x.f();    // error 
    x.g();    // works
}

where条款实际上做了什么?

天真的,我在想where Self: Sized;决定实施Trait的类型,如'如果你为Trait类型实现A你的类型A必须大小,即,它可以是i32但不是[i32]

但是,这样的约束更像是trait Trait: Sized(如果我错了,请纠正我)?

现在我注意到where Self: Sized;实际上确定我是否可以从f中调用gobject_safety_dynamic

我的问题:

  1. 幕后发生了什么?
  2. 什么(简单的英语)我实际上是通过where Self: Sized;告诉编译器使g()工作但f()不工作?
  3. 特别是:由于&self无论如何都是参考,fg之间存在各种(大小或未大小)类型的编译差异。它不会总是归结为像_vtable_f_or_g(*self) -> i32,无论where或类型是否大小?
  4. 为什么我可以为u8[u8]实现Trait。不应该编译器实际上阻止我为f()实现[u8],而不是在调用站点抛出错误?
syntax rust traits type-bounds
2个回答
3
投票

where子句实际上做了什么?

天真,我在想自我:大小;指示实现Trait的类型,比如'如果你为A类实现Trait你的类型A必须是大小的,即它可以是i32而不是[i32]。

然而,这种约束宁愿作为特质Trait:Sized

这是对的。

但是,在这种情况下,绑定仅适用于该函数。仅在呼叫站点检查where功能界限。

幕后发生了什么?

关于rust的语法有一个令人困惑的地方,Trait可以参考

  • 特质Trait;要么
  • “特质对象”Trait,实际上是一种类型,而不是一个对象。

Sized是一种特性,任何类型的T都是Sized,它的大小可以用std::mem::size_of::<T>()作为常数。这种类型不大的是str[u8],其内容没有固定的大小。

类型Trait也没有。直觉上,这是因为Trait作为一个类型由实现特征Trait的所有类型值组成,它们可能具有不同的大小。这意味着你永远不会有Trait类型的值 - 你只能通过“胖指针”来引用一个,例如&TraitBox<Trait>等等。它们的大小为2个指针 - 一个用于vtable,一个用于数据。看起来大致如下:

struct &Trait {
    pub data: *mut (),
    pub vtable: *mut (),
}

自动形式的impl:

impl Trait /* the trait */ for Trait /* the type */ {
    fn f(&self) -> i32 where Self: Sized { .. }
    fn g(&self) -> i32 {
        /* vtable magic: something like (self.vtable.g)(self.data) */
    }
}

什么(用简单的英语)我实际上告诉编译器Self:Sized;这使得g()工作但f()没有?

请注意,因为,正如我所提到的,Trait不是Sized,绑定的Self: Sized不满意所以函数f不能被称为Self == Trait

特别是:由于&self无论如何都是参考,对于各种(大小或未大小)类型,f和g之间存在编译差异。它不会总是归结为像_vtable_f_or_g(* self) - > i32这样的东西,无论在何处或类型是否大小?

Trait类型始终未标注。哪种类型被胁迫到Trait并不重要。使用Sized变量调用函数的方法是直接使用它:

fn generic<T: Trait + Sized>(x: &T) { // the `Sized` bound is implicit, added here for clarity
    x.f();  // compiles just fine
    x.g();
}

为什么我可以为u8和[u8]实现Trait。难道编译器实际上不应该阻止我为[u8]实现f(),而不是在调用站点抛出错误?

因为这个特性不受Self: Sized的限制 - 函数f是。所以没有什么可以阻止你实现这个功能 - 只是函数的界限永远不会被满足,所以你永远不能调用它。


3
投票

fn f(&self) -> i32 where Self: Sized;

这表示f仅针对也实现Sized的类型定义。 Unsized类型仍然可以实现Trait,但f将无法使用。

object_safety_dynamic内部,调用x.f()实际上是在做:(*x).f()。虽然x的大小是因为它是一个指针,但*x可能不是因为它可能是Trait的任何实现。但函数内部的代码必须适用于任何有效参数,因此不允许在那里调用x.f()

© www.soinside.com 2019 - 2024. All rights reserved.