内联常用方法

时间:2018-08-05 22:31:29

标签: rust inline

我想内联TraitA::x()方法而不知道谁实现了TraitA

例如:

(注意:StructB实现TraitA。)

let a: &TraitA = &StructB { x: 0f32 };
a.x(); // This should access `a.x` field directly
       // as `TraitA::x()` always return the same field,
       // the same `b.x` field slot.

该方法将在不同的结构中以相同的方式实现。

trait TraitA {
    fn x(&self) -> f32;
}

struct StructB {
    x: f32
}

impl TraitA for StructB {
    #[inline]
    fn x(&self) -> f32 {
        self.x
    }
}

a.x()会内联到(a as &StructB).x吗?

例如,我可以在C ++上做到这一点

class A {
    float _x;

public:
    float x() {
        return _x;
    }
};

struct B : A {};

int main() {
    A* a = new B;
    a->x();
}

2 个答案:

答案 0 :(得分:2)

我认为您在这里有些困惑。首先:在纯虚拟环境中使用特征时,不可能从特征中内联方法(也就是说:您没有有关实际类型的信息)。看起来像这样:

fn unknown_type(foo: &MyTrait) -> f32 {
    foo.x()
}

在这里,编译器可能无法知道特征对象foo后面的实际类型。因此,它被迫使用vtable并进行动态分配以调用该方法。有一种名为 devirtualization 的优化,它试图猜测正确的类型以进行静态分派(甚至是内联方法),但是这种优化有其局限性。

  

a.x()会内联到(a as &StructB).x吗?

在大多数情况下是的,但这与您的内联属性或特征对象无关。在您的小示例中,编译器可以看到整个函数,并且知道a具有基础类型StructB。但这又不是纯粹的虚拟上下文:编译器具有可以使用的类型信息。


另一件事:这一切与Java中的final / C#中的f没有关系-如the first version of your question中所述。

这些关键字仅对类层次结构有用。由于原则上可以覆盖派生类中Java / C#中每个类的所有方法,因此,从理论上讲,编译器永远不能内联或静态分派方法。它总是必须检查vtable来检查是否存在该方法的更专业版本。当编译器具有将该方法声明为final的类的变量时,由于可以保证不会被覆盖,因此可以静态调用它。

但是(在这个问题上)Rust中具有trait对象等同于具有接口类型的变量。而且,您(显然)不能将接口方法声明为final。因此在这里,无论某些实现类是否将其实现声明为final,编译器都必须执行虚拟调度。


此外,您的C ++与您的要求没有任何关系。基类A声明带有方法主体的非虚函数。这意味着该函数在虚拟上下文中将永远不可调用。但是在您的Rust代码中,x()没有主体,可以在虚拟上下文中使用。

在Rust中没有这样的东西,但是您可以通过impl Trait { ... }向trait对象添加方法。具有此特征的实现者无法覆盖这些方法,因此编译器可以执行静态分派或轻松地内联该方法。

您可以看到该示例及其程序集here


要回答我认为您实际上要问的问题:

  

您想内联该trait方法调用,以使其与具体类型的简单字段访问一样便宜/快速,对吧?

同样,在纯虚拟环境中是不可能的。当编译器不知道实现类型时,它不能生成简单的字段访问,因为它不知道该字段与基本指针的偏移量!我的意思是,并非所有实现类型都可以保证具有相同的内存布局,并且可以将x始终保持在相同的位置。

但是,它可能比一种方法做的更好:代替调用getter方法,可以将字段偏移量存储在vtable中,并使用该偏移量对字段进行索引。那仍然比标准的现场访问要贵,但比方法调用要快。

这正是this RFC: "Allow fields in traits that map to lvalues in impl'ing type"中的建议。 RFC线程已关闭,但RFC仍在开发中。

所以您现在不能这样做。使用特质方法是目前最好的方法。

答案 1 :(得分:1)

根据Can #[inline] be used in both trait method declarations and implementations?this thread on the language forum,不可能告诉编译器应内联特定方法的所有实现。但是,由于Rust是静态类型的,所以特征可以在编译时消除歧义,所以我看不出Rust编译器不能内联特定方法实现的理由。我不能肯定地说它是否会这样做,因为#[inline]属性只是编译器可以随意忽略的建议,但是即使我们在谈论独立函数而不是在讨论独立函数,这也是正确的方法。换句话说,内联方法应该是可能的,但在任何情况下都不应依赖于内联函数。