30天拿下Rust之面向對象(rust面向過程)
概述
在編程語言的世界中,Rust以其獨特的內(nèi)存安全、并發(fā)控制和高性能特性吸引了眾多開發(fā)者。雖然Rust并非傳統(tǒng)的面向對象編程語言(比如:C 、Java),但它依然支持并提供了一種頗具特色的面向對象編程方式,以實現(xiàn)類似于面向對象的編程范式。
在Rust中,沒有類的概念,但提供了模塊、結構體、枚舉、Trait來模擬面向對象編程的三大特性:封裝、繼承和多態(tài)。下面,我們分別進行介紹。
封裝
Rust的封裝機制提供了一種強大的方式來隱藏實現(xiàn)細節(jié),僅暴露必要的接口給使用者。封裝是面向對象編程的三大基本特性之一,它有助于創(chuàng)建模塊化的代碼,提高代碼的可維護性和安全性。在Rust中,封裝主要通過模塊、結構體、私有性等特性來實現(xiàn)。
Rust使用模塊來組織代碼,每個模塊都有自己的作用域,可以包含函數(shù)、類型定義、其他模塊等。模塊提供了一種自然的封裝方式,可以將相關的代碼組織在一起,并通過pub關鍵字來控制哪些內(nèi)容對外部可見。在之前的專欄文章中,我們曾專門介紹過模塊,故這里就不再進一步展開了。
在Rust中,結構體的字段可以通過指定訪問修飾符來控制其可見性。默認情況下,字段是私有的,這意味著它們只能在定義結構體的模塊內(nèi)部被訪問。通過pub關鍵字,我們可以將字段設置為公有,以便在外部訪問。
我們在下面的my_module.rs文件中聲明了公開的函數(shù)public_func和公開的結構體Publicstruct。private_func函數(shù)由于沒有使用pub關鍵字,默認為私有的。
// my_module.rspub fn public_func() { // ...}fn private_func() { // ...}pub struct PublicStruct { pub public_field: i32,}
接著,我們在下面的main.rs文件中使用了模塊my_module中的函數(shù)和結構體。由于private_func是私有的,因此,無法使用use關鍵字導入,編譯會提示錯誤:function `private_func` is private。
// main.rsmod my_module;use my_module::PublicStruct;use my_module::public_func;// 錯誤:該函數(shù)是私有的,無法使用use my_module::private_func;fn main() { let data = PublicStruct { public_field: 66 }; println!("{}", data.public_field); public_func();}
繼承
Rust并沒有傳統(tǒng)意義上的繼承機制,更傾向于使用組合和Trait來復用和擴展代碼。然而,通過一些模式,我們可以在Rust中實現(xiàn)類似繼承的效果。
使用組合模擬繼承
可以通過將一個類型作為另一個類型的字段來實現(xiàn)組合,這可以模擬繼承中子類包含父類字段的效果。
struct Parent { value: i32,}impl Parent { fn do_something(&self) { println!("parent data: {}", self.value); }}struct Child { parent: Parent, extra_value: String,}impl Child { fn new(value: i32, extra_field: String) -> Child { Child { parent: Parent { value }, extra_value: extra_field, } } fn do_child_thing(&self) { println!("child data: {}", self.extra_value); } // 委托給父類的方法 fn do_something(&self) { self.parent.do_something(); }}fn main() { let child = Child::new(66, "CSDN".to_string()); // 調(diào)用父類的方法 child.do_something(); // 調(diào)用子類的方法 child.do_child_thing();}
在上面的示例代碼中,Child結構體包含一個Parent類型的字段parent。這允許Child訪問和調(diào)用Parent的方法,從而模擬了繼承的行為。同時,Child還可以添加自己的字段和方法。
使用Trait模擬接口繼承
Trait在Rust中類似于接口,它們定義了一組方法簽名,可以由不同的類型來實現(xiàn),這可以模擬接口繼承的效果。
trait Animal { fn speak(&self);}struct Dog { name: String,}impl Animal for Dog { fn speak(&self) { println!("dog {} speak", self.name); }}struct Cat { name: String,}impl Animal for Cat { fn speak(&self) { println!("cat {} speak", self.name); }}fn animal_speak(animal: &dyn Animal) { animal.speak();}fn main() { let dog = Dog { name: "Buddy".to_string() }; let cat = Cat { name: "Whiskers".to_string() }; animal_speak(&dog); animal_speak(&cat);}
在上面的示例代碼中,我們定義了一個Animal特征,它有一個speak方法。Dog和Cat結構體都實現(xiàn)了Animal特征,因此它們都可以被視為動物,并且具有speak方法。通過動態(tài)分發(fā)(使用&dyn Animal),我們可以編寫接受任何實現(xiàn)了Animal特征的類型的函數(shù),比如這里的animal_speak。
多態(tài)
在Rust中,多態(tài)通常是通過Trait和泛型來實現(xiàn)的。多態(tài)允許我們編寫靈活的代碼,這些代碼可以處理多種不同的類型,只要這些類型滿足某些共同的接口或約束。Trait定義了類型必須實現(xiàn)的方法集合,從而允許我們編寫與這些類型交互的通用代碼。在上面介紹繼承的示例代碼中,我們已經(jīng)看到了基于Trait的多態(tài)實現(xiàn),故這里就不再贅述了。
接下來,我們使用泛型來實現(xiàn)多態(tài)。泛型允許我們編寫可以處理多種類型的函數(shù)或結構體,而不需要在編譯時指定具體的類型。
fn find_max<T: Ord>(slice: &[T]) -> &T { let mut max = &slice[0]; for item in slice.iter() { if item > max { max = item; } } max}fn main() { let numbers = vec![66, 99, 100, 50]; let max_number = *find_max(&numbers); println!("{}", max_number); let fruits = vec!["Lemon", "Apple", "Date"]; let max_fruit = find_max(&fruits); println!("{}", max_fruit);}
在上面的示例代碼中,我們定義了一個泛型函數(shù)find_max,它接受一個實現(xiàn)了Ord特征(即可以排序的類型)的切片,并返回其中的最大值。由于Ord特征是由多種標準庫類型實現(xiàn)的,我們可以使用這個函數(shù)來找出整數(shù)切片中的最大值,或字符串切片中基于字典序的最大字符串。
總結
雖然Rust并不是傳統(tǒng)意義上的面向對象編程語言,但它提供了豐富的工具來模擬和實現(xiàn)面向對象的概念。通過結構體與方法的組合、Trait與接口的定義、泛型的使用,Rust可以讓我們以面向對象的方式來組織和封裝代碼,實現(xiàn)高內(nèi)聚、低耦合的代碼結構。正是這種靈活性,使得Rust能夠適應各種復雜的編程需求,成為系統(tǒng)級編程的理想選擇。