3 个版本
0.0.1-alpha.2 | 2019 年 10 月 18 日 |
---|---|
0.0.1-alpha | 2019 年 9 月 15 日 |
0.0.0 | 2019 年 8 月 9 日 |
#2457 in Rust 模式
14KB
52 行
::继承
这个(实验性)crate 通过从组合派生委托,提供了获取“类似继承”行为的过程宏。
演示
想象一下有一个 Point
类型和一些行为/关联方法
#[derive(Debug, Clone, Copy, PartialEq)]
struct Point {
x: f32,
y: f32,
}
impl Point {
fn x (self: &'_ Self) -> f32
{
self.x
}
fn y (self: &'_ Self) -> f32
{
self.y
}
fn name (self: &'_ Self) -> Option<&'_ str>
{
Some("Point")
}
}
现在想象你想要一个新的 "Point
-like" 类型,但具有额外的属性(可能还有一些被覆盖的行为)
# struct Point;
struct NamedPoint {
name: String,
point: Point,
}
首先,要成为 "Point
-like",需要将固有方法抽象到一个特质中
#[derive(Debug, Clone, Copy, PartialEq)]
struct Point {
x: f32,
y: f32,
}
trait IsPoint {
fn x (self: &'_ Self) -> f32;
fn y (self: &'_ Self) -> f32;
fn name (self: &'_ Self) -> Option<&'_ str>;
}
impl IsPoint for Point {
fn x (self: &'_ Self) -> f32
{
self.x
}
fn y (self: &'_ Self) -> f32
{
self.y
}
fn name (self: &'_ Self) -> Option<&'_ str>
{
Some("Point")
}
}
现在我们想让 NamedPoint
实现 IsPoint
# #[derive(Debug,Clone,Copy,PartialEq)]struct Point{x:f32, y:f32}trait IsPoint{fn x(&self)->f32;fn y(&self)->f32;fn name(&self)->Option<&str>;}impl IsPoint for Point{fn x(&self)->f32{self.x}fn y(&self)->f32{self.y}fn name(&self)->Option<&str>{Some("Point")}}
struct NamedPoint {
name: String,
point: Point,
}
impl IsPoint for NamedPoint {
#[inline]
fn x (self: &'_ Self) -> f32
{
self.point.x()
}
#[inline]
fn y (self: &'_ Self) -> f32
{
self.point.y()
}
#[inline]
fn name (self: &'_ Self) -> Option<&'_ str>
{
self.point.name()
}
}
这会导致编写非常重复且无趣的(委托)代码...
介绍 ::inheritance
通过在 IsPoint
特质上添加 #[inheritable]
属性,并在 NamedPoint
结构体上添加 Inheritance
derive,可以完全跳过最后一个实现
use ::inheritance::{inheritable, Inheritance};
#[derive(Debug, Clone, Copy, PartialEq)]
struct Point {
x: f32,
y: f32,
}
#[inheritable]
trait IsPoint {
fn x (self: &'_ Self) -> f32;
fn y (self: &'_ Self) -> f32;
fn name (self: &'_ Self) -> Option<&'_ str>;
}
impl IsPoint for Point {
fn x (self: &'_ Self) -> f32
{
self.x
}
fn y (self: &'_ Self) -> f32
{
self.y
}
fn name (self: &'_ Self) -> Option<&'_ str>
{
Some("Point")
}
}
#[derive(Inheritance)]
struct NamedPoint {
name: String,
#[inherits(IsPoint)]
point: Point,
}
nightly
Rust
这个功能与 specialization
功能结合使用特别有趣,你已经在 nightly 中可以使用这个功能。
在 Cargo.toml
中依赖于 inheritance
crate 时,你可以指定你想要使用这个功能
[dependencies]
inheritance = { version = "...", features = ["specialization"] }
然后你将能够覆盖一些自动生成的委托方法
# use::inheritance::{inheritable, Inheritance};#[derive(Debug,Clone,Copy,PartialEq)]struct Point{x:f32, y:f32}#[inheritable]trait IsPoint{fn x(&self)->f32;fn y(&self)->f32;fn name(&self)->Option<&str>;}impl IsPoint for Point{fn x(&self)->f32{self.x}fn y(&self)->f32{self.y}fn name(&self)->Option<&str>{Some("Point")}}
#[derive(Inheritance)]
struct NamedPoint {
name: String,
#[inherits(IsPoint)]
point: Point,
}
# #[cfg(feature = "specialization")]
impl IsPoint for NamedPoint {
fn name (self: &'_ Self) -> Option<&'_ str>
{
Some(&*self.name)
}
}
更进一步
新类型
可以用于元组的结构体字段的 #[inherits(...)]
字段属性,使其成为新类型的完美工具
# use::inheritance::{inheritable, Inheritance};#[derive(Debug,Clone,Copy,PartialEq)]struct Point{x:f32, y:f32}#[inheritable]trait IsPoint{fn x(&self)->f32;fn y(&self)->f32;fn name(&self)->Option<&str>;}impl IsPoint for Point{fn x(&self)->f32{self.x}fn y(&self)->f32{self.y}fn name(&self)->Option<&str>{Some("Point")}}
#[derive(Inheritance)]
struct AnonymousPoint (
#[inherits(IsPoint)]
Point,
);
# #[cfg(feature = "specialization")]
impl IsPoint for AnonymousPoint {
fn name (self: &'_ Self) -> Option<&'_ str>
{
None
}
}
多重“继承”
具有 #[derive(Inheritance)]
注解的单个结构体可以在同一字段或多个不同字段上具有多个 #[inherits(...)]
注解,因为每个“继承”的特性实现只是将那个特性的方法委派到装饰的字段上。如果你尝试从多个字段“继承”相同的特性,则经典的可重复性规则将阻止它
# use::inheritance::{inheritable, Inheritance};#[derive(Debug,Clone,Copy,PartialEq)]struct Point{x:f32, y:f32}#[inheritable]trait IsPoint{fn x(&self)->f32;fn y(&self)->f32;fn name(&self)->Option<&str>;}impl IsPoint for Point{fn x(&self)->f32{self.x}fn y(&self)->f32{self.y}fn name(&self)->Option<&str>{Some("Point")}}
#[derive(Debug, Clone, Copy, PartialEq)]
enum Color {
Red,
Green,
Blue,
}
#[inheritable]
trait Colored {
fn color (self: &'_ Self) -> Color;
}
impl Colored for Color {
#[inline]
fn color (self: &'_ Self) -> Color
{
*self
}
}
#[derive(Inheritance)]
struct ColoredPoint {
#[inherits(IsPoint)]
point: Point,
#[inherits(Colored)]
pigments: Color,
}
虚函数分发
“虚”函数背后的想法,类似于 C++,是对象始终携带一个包含某些方法的 vtable(例如标记为 virtual
的方法),而其他方法则不在其中。然而,在 Rust 中,“对象”有时携带一个 vtable(即,结构体和枚举不携带,但 trait 对象 dyn Trait
携带),并且这个 vtable 包含该特性和其超特质的全部方法
想象有一个 .present()
方法,其方法体调用 .name()
,并且每次调用 .name()
时,其行为/返回值都会因不同的 Point
而异。
/// somewhere in the code
fn present (self: &'_ Self) -> Cow<'static, str>
// where Self : IsPoint
{
if let Some(name) = self.name() {
Cow::from(format!("{}({}, {})", name, self.x(), self.y()))
} else {
Cow::from("<anonymous>")
}
}
let point = Point { x: 42., y: 27. };
assert_eq!(
&point.present() as &str,
"Point(42.0, 27.0)"
);
let anonymous_point = AnonymousPoint(point);
assert_eq!(
&anonymous_point.present() as &str,
"<anonymous>"
);
let named_point = NamedPoint { name: "Carré", point };
assert_eq!(
&named_point.present() as &str,
"Carré(42.0, 27.0)"
);
-
在 C++ 这样的语言中,可以通过将
.name()
方法标记为virtual
来实现这一点,并且通过将.present()
作为基类的方法来实现(不一定本身是virtual
)。 -
在 Rust 中,上述策略无法实现;必须使用函数/方法,该函数/方法在
IsPoint
类型的实现者上是多态的,使用-
动态分发(C++ 中
virtual
方法的背后机制)# use::inheritance::{inheritable, Inheritance};#[derive(Debug,Clone,Copy,PartialEq)]struct Point{x:f32, y:f32}#[inheritable]trait IsPoint{fn x(&self)->f32;fn y(&self)->f32;fn name(&self)->Option<&str>;}impl IsPoint for Point{fn x(&self)->f32{self.x}fn y(&self)->f32{self.y}fn name(&self)->Option<&str>{Some("Point")}}use::std::borrow::Cow; impl dyn IsPoint + '_ { fn present (self: &'_ Self) -> Cow<'static, str> { if let Some(name) = self.name() { Cow::from(format!("{}({}, {})", name, self.x(), self.y())) } else { Cow::from("<anonymous>") } } }
-
静态分发;然后要获得类似方法的语法,需要辅助特异
# use::inheritance::{inheritable, Inheritance};#[derive(Debug,Clone,Copy,PartialEq)]struct Point{x:f32, y:f32}#[inheritable]trait IsPoint{fn x(&self)->f32;fn y(&self)->f32;fn name(&self)->Option<&str>;}impl IsPoint for Point{fn x(&self)->f32{self.x}fn y(&self)->f32{self.y}fn name(&self)->Option<&str>{Some("Point")}}use::std::borrow::Cow; trait Present where Self : IsPoint, { fn present (self: &'_ Self) -> Cow<'static, str> { if let Some(name) = self.name() { Cow::from(format!("{}({}, {})", name, self.x(), self.y())) } else { Cow::from("<anonymous>") } } } impl<T : ?Sized> Present for T where T : IsPoint, {}
-
当前宏不针对功能齐全的“虚”方法,因此尚不能直接 重写 .present()
由于这是一个实验性软件包,我将尝试探索这一可能性...
依赖项
~2MB
~46K SLoC