Rust 中的繼承與代碼複用

在學習Rust過程當中忽然想到怎麼實現繼承,特別是用於代碼複用的繼承,因而在網上查了查,發現不是那麼簡單的。java

C++的繼承

首先看看c++中是如何作的。node

例如要作一個場景結點的Node類和一個Sprite類繼承它。c++

定義一個node基類git

struct Node {
    float x;
    float y;
    void move_to(float x, float y) {
        this->x = x;
        this->y = y;
    }
    virtual void draw() const {
        printf("node: x = %f, y = %f\n", x, y);
    }
};

再定義一個子類Sprite,重載draw方法:github

struct Sprite: public Node {
    virtual void draw() const {
        printf("sprite: x = %f, y = %f\n", x, y);
    }
};

能夠把sprite做爲一個Node來使用,而且能夠重用Node中的move_to函數:函數

Node* sprite = new Sprite();
sprite->move_to(10, 10);
sprite->draw();

Rust中的繼承

如今要用Rust作一樣的事。定義一個Node基類:學習

struct Node {
    x: f32,
    y: f32,
}

impl Node {
    fn draw(&self) {
        println!("node: x={}, y={}", self.x, self.y)
    }

    fn move_to(&mut self, x: f32, y: f32) {
        self.x = x;
        self.y = y;
    }
}

定義子類的時候咱們遇到了麻煩:Rust裏struct是不能繼承的!this

struct Sprite: Node;

這麼寫會報錯:code

error: `virtual` structs have been removed from the language

virtual struct是什麼東西?原來Rust曾經有一個virtual struct的特性可使struct繼承另外一個struct,可是被刪掉了:(
RFC在這裏。如今Rust的struct是不能繼承的了。繼承

使用 trait

Rust 裏的 trait 是相似於 java 裏 interface,能夠繼承的。咱們把 Node 定義爲 trait。

trait Node {
    fn move_to(&mut self, x: f32, y: f32);
    fn draw(&self);
}

但咱們發現沒有辦法在 Node 中實現 move_to 方法,由於 trait 中不能有成員數據:x, y。

那隻好在每一個子類中寫各自的方法實現,例如咱們須要一個空Node類和一個Sprite類:

struct EmptyNode {
    x: f32,
    y: f32,
}

impl Node for EmptyNode {
    fn draw(&self) {
        println!("node: x={}, y={}", self.x, self.y)
    }

    fn move_to(&mut self, x: f32, y: f32) {
        self.x = x;
        self.y = y;
    }
}

struct Sprite {
    x: f32,
    y: f32,
}

impl Node for Sprite {
    fn draw(&self) {
        println!("sprite: x={}, y={}", self.x, self.y)
    }

    fn move_to(&mut self, x: f32, y: f32) {
        self.x = x;
        self.y = y;
    }
}

是否是以爲有大量代碼重複了?Sprite只須要重寫 draw方法,但要把全部方法都實現一遍。若是要實現不少種 Node,每種都要實現一遍,那就要寫吐血了。

組合

組合是一個代碼重用的好方法。要重用代碼時,組合並且比繼承更能體現「has-a」的關係。咱們把 Node 從新定義爲以前的 struct 基類,而後把 Node 放在 Sprite 中:

struct Node {
    x: f32,
    y: f32,
}

impl Node {
    fn draw(&self) {
        println!("node: x={}, y={}", self.x, self.y)
    }

    fn move_to(&mut self, x: f32, y: f32) {
        self.x = x;
        self.y = y;
    }
}

struct Sprite {
    node: Node
}

impl Sprite {
    fn draw(&self) {
        println!("sprite: x={}, y={}", self.node.x, self.node.y)
    }

    fn move_to(&mut self, x: f32, y: f32) {
        self.node.move_to(x, y);
    }
}

清爽了很多,美中不足的是還不能省略 move_to 方法,還要手動寫一遍,簡單調用 Node 中的同名方法。

組合和繼承還有一些不一樣的,好比不能把 Sprite 轉型爲 Node。

Deref & DerefMut trait

std::ops::Deref 用於重載取值運算符: *。這個重載能夠返回其餘類型,正好能夠解決組合中不能轉換類型的問題。

在這個例子中,因爲 move_to 的 self 可變的,因此要實現 Deref 和 DerefMut

struct Sprite {
    node: Node
}

impl Sprite {
    fn draw(&self) {
        println!("sprite: x={}, y={}", self.node.x, self.node.y)
    }
}

impl Deref for Sprite {
    type Target = Node;

    fn deref<'a>(&'a self) -> &'a Node {
        &self.node
    }
}

impl DerefMut for Sprite {
    fn deref_mut<'a>(&'a mut self) -> &'a mut Node {
        &mut self.node
    }
}

以後就能夠把 &Sprite 轉換爲 &Node

let mut sprite = Sprite{ node: Node { x: 10.0, y: 20.0 } };
let mut sprite_node: &mut Node = &mut sprite;
sprite_node.move_to(100.0, 100.0);

要注意的是對sprite_node的方法調用重載是不起做用的。若是 sprite_node.draw(),調用的仍是Node.draw(),而不是Sprite.draw()。

若是要調用子類的方法,必須有子類類型的變量來調用。

let mut sprite = Sprite{ node: Node { x: 10.0, y: 20.0 } };

// 這個大括號限制 mut borrow 範圍
{
    let mut sprite_node: &mut Node = &mut sprite;
    sprite_node.move_to(100.0, 100.0);
    sprite.node.draw(); // 輸出 node: x=100, y=100
} 

sprite.draw(); // 輸出 sprite: x=100, y=100
相關文章
相關標籤/搜索