在學習Rust過程當中忽然想到怎麼實現繼承,特別是用於代碼複用的繼承,因而在網上查了查,發現不是那麼簡單的。java
首先看看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作一樣的事。定義一個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是不能繼承的了。繼承
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。
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