我有多种类型的方法.我想通过编写一个接口来抽象它们,就像我在Java中一样:
public interface Shape {
public float area();
}
class Circle implements Shape {
public float area() {
return radius * radius * Math.PI;
}
public float radius;
}
Run Code Online (Sandbox Code Playgroud)
但是,interfaceRust中没有关键字.Rust不提供抽象多种类型的可能性吗?
Fre*_*ios 24
TL; DR:最接近Rust的接口是一个特性.不过,千万不能指望它在所有点的界面相似.我的回答并非旨在详尽无遗,而是提供一些与其他语言相比较的元素.
如果你想要一个类似于接口的抽象,你需要使用Rust的trait:
trait Shape {
fn area(&self) -> f32;
}
struct Circle {
radius: f32,
}
impl Shape for Circle {
fn area(&self) -> f32 {
self.radius.powi(2) * std::f32::consts::PI
}
}
struct Square {
side: f32,
}
impl Shape for Square {
fn area(&self) -> f32 {
self.side.powi(2)
}
}
fn main() {
display_area(&Circle { radius: 1. });
display_area(&Square { side: 1. });
}
fn display_area(shape: &dyn Shape) {
println!("area is {}", shape.area())
}
Run Code Online (Sandbox Code Playgroud)
但是,将Rust特性视为OOP接口的等效项是错误的.我将列举Rust的一些特殊情况trait.
在Rust中,调度(即在给定特征时使用正确的数据和方法)可以通过两种方式完成:
当静态调度特征时,运行时没有开销.这相当于C++模板; 但是在C++使用SFINAE的地方,Rust编译器使用我们给他的"提示"检查有效性:
fn display_area(shape: &impl Shape) {
println!("area is {}", shape.area())
}
Run Code Online (Sandbox Code Playgroud)
有了impl Shape,我们说,我们的功能具有实现泛型类型参数编译器Shape,所以我们可以使用的方法Shape::area上我们shape.
在这种情况下,与C++模板一样,编译器将为传入的每个不同类型生成不同的函数.
在我们的第一个例子:
fn display_area(shape: &dyn Shape) {
println!("area is {}", shape.area())
}
Run Code Online (Sandbox Code Playgroud)
调度是动态的.这相当于在C#/ Java中使用接口或在C++中使用抽象类.
在这种情况下,编译器不关心类型shape.正确的做法将在运行时确定,通常成本非常低.
如您所见,数据与实现分开; 例如,C#扩展方法.此外,特征的一个实用程序是扩展值的可用方法:
trait Hello {
fn say_hello(&self);
}
impl Hello for &'static str {
fn say_hello(&self) {
println!("Hello, {}!", *self)
}
}
fn main() {
"world".say_hello();
}
Run Code Online (Sandbox Code Playgroud)
这样做的一大优点是,您可以在不修改数据的情况下为数据实现特征.相反,在经典的面向对象语言中,您必须修改类以实现另一个接口.
这种分离在最低层也是如此.在动态调度的情况下,该方法有两个指针:一个用于数据,另一个用于方法(vtable).
该特性还有一个比经典接口更多的东西:它可以提供方法的默认实现(就像Java 8中的"defender"方法一样).例:
trait Hello {
fn say_hello(&self) {
println!("Hello there!")
}
}
impl Hello for i32 {}
fn main() {
123.say_hello(); // call default implementation
}
Run Code Online (Sandbox Code Playgroud)
要使用经典的OOP单词,这就像一个没有变量成员的抽象类.
Rust特征的系统不是继承系统.例如,您不能尝试向下转换,或尝试将特征的引用转换为另一个特征.要获得有关此信息的更多信息,请参阅有关向上转换的此问题.
此外,您可以使用动态类型来模拟您想要的某些行为.
虽然您可以使用各种技巧来模拟Rust中的继承机制,但使用惯用设计而不是将语言转换为外部思维方式更好的想法,这将无用地增加代码的复杂性.
您应该阅读Rust书中关于特征的章节,以了解有关此主题的更多信息.
| 归档时间: |
|
| 查看次数: |
3482 次 |
| 最近记录: |