2个版本
0.0.1-alpha.2 | 2019年10月18日 |
---|---|
0.0.1-alpha | 2019年9月15日 |
在#避免中排名11
每月下载量22
18KB
465 行
::继承
这个(实验性)crate提供过程宏,通过从组合派生委托来实现“继承-like”行为。
展示
想象有一些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
派生,可以完全跳过最后一个实现
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
这个特性与nightly中的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(即,struct
和enum
不携带,但特例对象(dyn Trait
)携带),并且这个vtable包含特性的所有方法和其超特性的方法
想象一个具有 .present()
方法的结构,该方法的主体调用 .name()
,并且每个不同的 Point
都会改变由 .name()
调用返回的行为/值。
/// 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
~45K SLoC