rust - 内联一个常用方法
问题描述
我想在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();
}
解决方案
我认为你在这里混淆了一些事情。首先:当您在纯虚拟上下文中使用它时,不可能从特征中内联方法(即:您没有关于实际类型的信息)。这看起来像这样:
fn unknown_type(foo: &MyTrait) -> f32 {
foo.x()
}
在这里,编译器不可能知道 trait object 背后的实际类型foo
。因此,它被迫使用vtable并进行动态调度来调用该方法。有一种称为去虚拟化的优化,它试图猜测正确的类型来进行静态调度(甚至内联方法),但这种优化有其局限性。
会
a.x()
内联到(a as &StructB).x
?
在大多数情况下是的,但这与您的 inline-attribute 或 trait 对象无关。在您的小示例中,编译器可以看到整个函数并知道它a
具有底层类型StructB
。但同样,这不是一个纯粹的虚拟上下文:编译器有它可以使用的类型信息。
另一件事:这一切都与final
Java/ f
C# 中的所有内容无关——正如您的问题的第一个版本中所述。
这些关键字仅对类层次结构有用。由于原则上您可以在派生类中覆盖 Java/C# 中每个类的所有方法,因此编译器理论上永远不会内联或静态调度方法。它总是必须检查 vtable 以检查是否有此方法的更专业版本。当编译器有一个类的变量将该方法声明为final
时,它可以静态调用它,因为它保证它不会被覆盖。
但是在 Rust 中拥有一个 trait 对象(关于这个问题)等同于拥有一个具有接口类型的变量。而且您(显然)不能将接口方法声明为final
. 所以在这里,无论某些实现类是否将它们的实现声明为final
.
此外,您的 C++ 与您的要求没有任何关系。基类A
声明了一个带有方法体的非虚函数。这意味着该函数将永远无法在虚拟上下文中调用。但是在您的 Rust 代码x()
中没有主体,可以在虚拟上下文中使用。
在 Rust 中没有类似的东西,但你可以通过impl Trait { ... }
. 该特征的实现者无法覆盖这些方法,因此编译器可以轻松地进行静态调度或内联方法。
您可以在此处查看该示例及其程序集。
要回答我认为您实际上在问的问题:
您想内联该特征方法调用,使其与具体类型的简单字段访问一样便宜/快速,对吗?
同样,这在纯虚拟环境中是不可能的。当编译器不知道实现类型时,它无法生成简单的字段访问,因为它不知道该字段相对于基指针的偏移量!我的意思是,并非所有实现类型都保证具有相同的内存布局并x
始终保持在相同的位置。
但是,可以比方法做得更好:而不是调用 getter 方法,可以将字段偏移量存储在 vtable 中并使用它来索引字段。这仍然比标准字段访问更昂贵,但比方法调用更快。
这正是本 RFC 中提出的内容:“允许特征中的字段映射到 impl'ing 类型中的左值”。RFC 线程已关闭,但 RFC 仍在开发中。
所以你现在不能这样做。使用 trait 方法是你现在能做的最好的事情。
推荐阅读
- typescript - 承诺打字稿上的抛出错误失败
- c# - Windows Form c#在同一个表单上读取和写入同一个文件错误?
- wso2 - 发布带有禁用分块的空正文时 wso2am 超时
- typescript - 解决不属于 import 语句的 require 语句
- react-native - 有没有办法将 RCTAnimation 模块添加到我的 React 本机项目
- c++ - 使用互斥体而不使用条件变量的两个线程之间的 C++ 同步
- ios - 无法将值存储到全局数组 SWIFT
- ajax - 验证失败应防止关闭模式对话框
- r - R数据帧重排
- postgresql - 使用 dokku postgres 插件存储的数据在多大程度上是持久的?