Rust從入門到精通07-trait
Rust 語言中,trait 是一個(gè)非常重要的概念,可以包含:函數(shù)、常量、類型等。
通俗一點(diǎn)理解,trait 以一種抽象的方式定義共享的行為,可以被認(rèn)為是一些語言的接口,但是與接口也有一定區(qū)別,下面會(huì)介紹。
1、成員方法
trait 中可以定義方法。
trait Shape {
fn area(&self) -> f64;
}
我們?cè)谝粋€(gè)名為 Shape 的 trait 中定義了一個(gè)方法 area。
1.1 方法參數(shù)
看上面定義的 Shape,方法的參數(shù)是 &self。
其實(shí)對(duì)于每個(gè) trait 都有一個(gè)隱藏的類型 Self(大寫的 S),代表實(shí)現(xiàn)此 trait 的具體類型。
Rust 中 Self 和 self 都是關(guān)鍵字,大寫的Self是類型名,小寫的 self 是變量名。
其實(shí) area(&self) 等價(jià)于 area(self : &Self),只不過 rust 提供了簡(jiǎn)化的寫法。
下面幾種情況都是等價(jià)的。
trait T {
fn method1(self : Self);
fn method2(self : &Self);
fn method3(self : &mut Self);
}
//等價(jià)于下面方法定義
trait T {
fn method1(self);
fn method2(&self);
fn method3(&mut self);
}
1.2 調(diào)用實(shí)例
可以參考如下例子:
trait Shape {
fn area(&self) -> f64;
}
struct Circle {
radius : f64,
}
impl Shape for Circle {
// Self 的類型就是 Circle
fn area(self : &Self) -> f64{
// 可以通過self.radius訪問成員變量
std::f64::consts::PI * self.radius * self.radius
}
}
fn main() {
let circle = Circle{ radius : 2f64};
println!("The area is {}",circle.area())
}
①、通過 self.成員變量 來訪問成員變量;
②、通過 實(shí)例.成員方法 來調(diào)用成員方法;
2、匿名 trait
impl Circle {
fn get_radius(&self) -> f64 {
self.radius
}
}
impl 關(guān)鍵字后面直接接類型,沒有 trait 的名字。
可以將上面代碼看成是為 Circle 實(shí)現(xiàn)了一個(gè)匿名的 trait。
3、 靜態(tài)方法
靜態(tài)方法:第一個(gè)參數(shù)不是 self 參數(shù)的方法。
impl Circle {
// 普通方法
fn get_radius(&self) -> f64 {
self.radius
}
// 靜態(tài)方法
fn get_area(this : &Self) ->f64 {
std::f64::consts::PI * this.radius * this.radius
}
}
fn main() {
let c = Circle{ radius : 2f64};
// 調(diào)用普通方法
println!("The radius is {}",c.radius);
// 調(diào)用靜態(tài)方法
println!("The area is {}",Circle::get_area(&c))
}
注意和普通方法的區(qū)別,參數(shù)命名不同,以及調(diào)用方式不同(普通方法是小數(shù) 實(shí)例.方法 ,靜態(tài)方法是 類型::方法 )。
靜態(tài)方法的調(diào)用可以 Type::FunctionName()。
4、擴(kuò)展方法
利用 trait 給其它類型添加方法。
比如我們給內(nèi)置類型 i32 添加一個(gè)方法:
// 擴(kuò)展方法
trait Double {
fn double(&self) -> Self;
}
impl Double for i32 {
fn double(&self) -> i32{
self * 2
}
}
fn main() {
let x : i32 = 10.double();
println!("x double is {}",x);//20
}
5、泛型約束
在Rust中,靜態(tài)分發(fā)(Static Dispatch)和動(dòng)態(tài)分發(fā)(Dynamic Dispatch)是用于選擇和調(diào)用函數(shù)的兩種不同的機(jī)制。
5.1 靜態(tài)分發(fā)
在編譯時(shí)確定函數(shù)調(diào)用的具體實(shí)現(xiàn)。
它通過在編譯階段解析函數(shù)調(diào)用并選擇正確的函數(shù)實(shí)現(xiàn),從而實(shí)現(xiàn)高效的調(diào)用。
靜態(tài)分發(fā)通常適用于使用泛型的情況,其中編譯器可以根據(jù)具體的類型參數(shù)確定調(diào)用的函數(shù)。
fn main() {
fn myPrint<T: ToString>(v: T) {
v.to_string();
}
let c = 'a';
let s = String::from("hello");
myPrint::<char>(c);
myPrint::<String>(s);
}
等價(jià)于:
fn myPrint(c:char){
c.to_string();
}
fn myPrint(str:String){
str.to_string();
}
5.2 動(dòng)態(tài)分發(fā)
在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類型來選擇函數(shù)的實(shí)現(xiàn)。
它適用于使用trait對(duì)象(通過使用dyn關(guān)鍵字)的情況,其中編譯器在編譯階段無法確定具體的函數(shù)實(shí)現(xiàn)。
在運(yùn)行時(shí),程序會(huì)根據(jù)trait對(duì)象所包含的實(shí)際類型來動(dòng)態(tài)地選擇要調(diào)用的函數(shù)。
動(dòng)態(tài)分發(fā)提供了更大的靈活性,但相對(duì)于靜態(tài)分發(fā),它可能會(huì)帶來一些運(yùn)行時(shí)開銷。
下面代碼分別演示靜態(tài)分發(fā)和動(dòng)態(tài)分發(fā)的區(qū)別:
trait Animal {
fn make_sound(&self);
}
struct Cat;
struct Dog;
impl Animal for Cat {
fn make_sound(&self) {
println!("Meow!");
}
}
impl Animal for Dog {
fn make_sound(&self) {
println!("Woof!");
}
}
fn static_dispatch(animal: &impl Animal) {
animal.make_sound();
}
fn dynamic_dispatch(animal: &dyn Animal) {
animal.make_sound();
}
fn main() {
let cat = Cat;
let dog = Dog;
// 靜態(tài)分發(fā)
static_dispatch(&cat);
static_dispatch(&dog);
// 動(dòng)態(tài)分發(fā)
dynamic_dispatch(&cat as &dyn Animal);
dynamic_dispatch(&dog as &dyn Animal);
}
5、一致性原則
一致性原則,也稱為孤兒原則(Orphan Rule):
Impl 塊要么與 trait 塊的聲明在同一個(gè) crate 中,要么與類型的聲明在同一個(gè) crate 中。
孤兒原則(Orphan Rule)是Rust語言中的一項(xiàng)重要設(shè)計(jì)原則,它有助于確保trait實(shí)現(xiàn)的可控性和可追溯性。遵守孤兒原則可以提高代碼的可讀性和可維護(hù)性,并降低潛在的沖突和混亂。
也就是說如果 trait 來自外部,而且類型也來自外部 crate,編譯器是不允許你為這個(gè)類型 impl 這個(gè) trait。它們當(dāng)中至少有一個(gè)是在當(dāng)前 crate 中定義的。
比如下面兩種情況都是可以的:
use std::fmt::Display;
struct A;
impl Display for A {}
trait TraitA {}
impl TraitA for u32 {}
但是下面這種情況就不可以:
use std::fmt::Display;
impl Display for u32 {}

這也給我們提供了一個(gè)標(biāo)準(zhǔn):上游開發(fā)者在寫庫的時(shí)候,一些比較常用的標(biāo)準(zhǔn) trait,如 Display/Debug/ToString/Default 等,應(yīng)該盡可能的提供好。
否則下游使用這個(gè)庫的開發(fā)者是沒法幫我們實(shí)現(xiàn)這些 trait 的。
6、trait 和 接口區(qū)別
開篇我們說為了便于理解 trait,可以想象為其它語言,比如Java中的接口。但是實(shí)際上他們還是有很大的區(qū)別的。
因?yàn)?rust 是一種用戶可以對(duì)內(nèi)存有著精確控制的強(qiáng)類型語言。在目前 Rust 版本中規(guī)定:
函數(shù)傳參類型,返回值類型等都是要在編譯期確定大小的。
而 trait 本身既不是具體類型,也不是指針類型,它只是定義了針對(duì)類型的、抽象的約束。不同的類型可以實(shí)現(xiàn)同一個(gè) trait,滿足同一個(gè) trait 的類型可能具有不同的大小。
所以 trait 在編譯階段沒有固定的大小,我們不能直接使用 trait 作為實(shí)例變量、參數(shù)以及返回值。
類似下面的寫法都是錯(cuò)誤的:
trait Shape {
fn area(&self) -> f64;
}
impl Circle {
//錯(cuò)誤1: trait(Shape)不能做參數(shù)的類型
fn use_shape(arg : Shape){
}
//錯(cuò)誤2: trait(Shape)不能做返回值的類型
fn ret_shape() -> Shape{
}
}
fn main() {
// 錯(cuò)誤3:trait(Shape)不能做局部變量的類型
let x : Shape = Circle::new();
}
可以看到編譯器的錯(cuò)誤提示:

7、derive
Rust 標(biāo)準(zhǔn)庫內(nèi)部實(shí)現(xiàn)了一些邏輯較為固定的 trait,通過 derive 配置可以幫助我們自動(dòng) impl 某些 trait,而無需手動(dòng)編寫對(duì)應(yīng)的代碼。
#[derive(Debug)]
struct Foo {
data : i32,
}
fn main() {
let v1 = Foo{data : 0};
println!("{:?}",v1)
}
加上 Debug 的trait 實(shí)現(xiàn),便于格式化打印 struct。
[derive(Debug)] 等價(jià)于 impl Debug for Foo {}
目前,Rust 支持的可以自動(dòng) derive 的 trait 有如下:
Copy,Clone,Default,Hash,
Debug,PartialEq,Eq,PartialOrd,
Ord,RustcEncodable,RustcDecodable,
FromPrimitive,Send,Sync
8、標(biāo)準(zhǔn)庫中常見 trait
在介紹 derive 時(shí),我們說明了內(nèi)置的一些 trait,這都是標(biāo)準(zhǔn)庫中比較常見的 trait,下面我們分別介紹這些 trait 是干什么的。
8.1 Display 和 Debug
可以分別看下源碼定義:
【Display】
pub trait Display {
/// Formats the value using the given formatter.
///
/// # Examples
///
/// ```
/// use std::fmt;
///
/// struct Position {
/// longitude: f32,
/// latitude: f32,
/// }
///
/// impl fmt::Display for Position {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "({}, {})", self.longitude, self.latitude)
/// }
/// }
///
/// assert_eq!("(1.987, 2.983)",
/// format!("{}", Position { longitude: 1.987, latitude: 2.983, }));
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
}
【Debug】
pub trait Debug {
/// Formats the value using the given formatter.
///
/// # Examples
///
/// ```
/// use std::fmt;
///
/// struct Position {
/// longitude: f32,
/// latitude: f32,
/// }
///
/// impl fmt::Debug for Position {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// f.debug_tuple("")
/// .field(&self.longitude)
/// .field(&self.latitude)
/// .finish()
/// }
/// }
///
/// let position = Position { longitude: 1.987, latitude: 2.983 };
/// assert_eq!(format!("{:?}", position), "(1.987, 2.983)");
///
/// assert_eq!(format!("{:#?}", position), "(
/// 1.987,
/// 2.983,
/// )");
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
}
①、只有實(shí)現(xiàn)了 Display trait 的類型,才能夠用 {} 格式打印出來。
②、只有實(shí)現(xiàn)了 Debug trait 的類型,才能夠用{:?} {:#?} 格式打印出來。
這兩者區(qū)別如下:
1、Display 假定了這個(gè)類型可以用 utf-8 格式的字符串表示,它是準(zhǔn)備給最終用戶看的,并不是所有的類型都應(yīng)該或者能夠?qū)崿F(xiàn)這個(gè) trait。這個(gè) trait 的 fmt 應(yīng)該如何格式化字符串,完全取決于程序員自己,編譯器不提供自動(dòng) derive 的功能。
2、標(biāo)準(zhǔn)庫中還有一個(gè)常用 trait 叫作 std::string::ToString,對(duì)于所有實(shí)現(xiàn)了 Display trait 的類型,都自動(dòng)實(shí)現(xiàn)了這個(gè) ToString trait 。它包含了一個(gè)方法 to_string(&self) -> String。任何一個(gè)實(shí)現(xiàn)了 Display trait 的類型,我們都可以對(duì)它調(diào)用 to_string() 方法格式化出一個(gè)字符串。
3、Debug 則主要是為了調(diào)試使用,建議所有的作為 API 的“公開”類型都應(yīng)該實(shí)現(xiàn)這個(gè) trait,以方便調(diào)試。它打印出來的字符串不是以“美觀易讀”為標(biāo)準(zhǔn),編譯器提供了自動(dòng) derive 的功能。
struct Color{
r:u8,
g:u8,
b:u8,
}
impl Default for Color{
fn default() -> Self{
Self{r:0,g:0,b:0}
}
}
等價(jià)于:
#[derive(Default)]
struct Color{
r:u8,
g:u8,
b:u8,
}
8.2 ToString
ToString 是 Rust 標(biāo)準(zhǔn)庫中定義的一個(gè)非常常用的 trait,它的目的是將任何實(shí)現(xiàn)了它的類型轉(zhuǎn)換為 String 類型的文本表示
#[cfg_attr(not(test), rustc_diagnostic_item = "ToString")]
#[stable(feature = "rust1", since = "1.0.0")]
pub trait ToString {
/// Converts the given value to a `String`.
///
/// # Examples
///
/// ```
/// let i = 5;
/// let five = String::from("5");
///
/// assert_eq!(five, i.to_string());
/// ```
#[rustc_conversion_suggestion]
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "to_string_method")]
fn to_string(&self) -> String;
}
自動(dòng)實(shí)現(xiàn)
雖然 ToString 是一個(gè) trait,但你幾乎不需要手動(dòng)實(shí)現(xiàn)它,因?yàn)闃?biāo)準(zhǔn)庫中已經(jīng)為所有實(shí)現(xiàn)了 Display 的類型,自動(dòng)實(shí)現(xiàn)了 ToString。
也就是說:
實(shí)現(xiàn)了
Display? 自動(dòng)擁有.to_string()方法。
to_string()本質(zhì)上等價(jià)于format!("{}", value)。
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: fmt::Display + ?Sized> ToString for T {
#[inline]
fn to_string(&self) -> String {
<Self as SpecToString>::spec_to_string(self)
}
}
impl<T: fmt::Display + ?Sized> SpecToString for T {
// A common guideline is to not inline generic functions. However,
// removing `#[inline]` from this method causes non-negligible regressions.
// See <https://github.com/rust-lang/rust/pull/74852>, the last attempt
// to try to remove it.
#[inline]
default fn spec_to_string(&self) -> String {
let mut buf = String::new();
let mut formatter =
core::fmt::Formatter::new(&mut buf, core::fmt::FormattingOptions::new());
// Bypass format_args!() to avoid write_str with zero-length strs
fmt::Display::fmt(self, &mut formatter)
.expect("a Display implementation returned an error unexpectedly");
buf
}
}
8.3 ParitialEq/Eq
在Rust中,PartialOrd、Ord、PartialEq和Eq是用于比較和排序的trait。通過使用derive宏,可以自動(dòng)為結(jié)構(gòu)體或枚舉實(shí)現(xiàn)這些trait的默認(rèn)行為。
下面是對(duì)這些trait的簡(jiǎn)要解釋:
PartialOrdtrait:用于部分順序比較,即可以進(jìn)行比較但不一定可以完全排序。它定義了partial_cmp方法,用于比較兩個(gè)值并返回一個(gè)Option<Ordering>枚舉,表示比較結(jié)果。Ordtrait:用于完全順序比較,即可以進(jìn)行完全排序。它是PartialOrdtrait的超集,定義了cmp方法,用于比較兩個(gè)值并返回Ordering枚舉,表示比較結(jié)果。PartialEqtrait:用于部分相等性比較。它定義了eq、ne、lt、le、gt和ge等方法,用于比較兩個(gè)值是否相等、不相等、小于、小于等于、大于、大于等于。Eqtrait:用于完全相等性比較,即可以進(jìn)行完全相等性判斷。它是PartialEqtrait的超集,無需手動(dòng)實(shí)現(xiàn),通過自動(dòng)實(shí)現(xiàn)PartialEqtrait即可獲得Eqtrait的默認(rèn)實(shí)現(xiàn)。
Eq定義為PartialEq的subtrait
#[derive(PartialEq, Debug)] // 注意這一句
struct Point {
x: i32,
y: i32,
}
fn example_assert(p1: Point, p2: Point) {
assert_eq!(p1, p2); // 比較
}
8.4 PartialOrd/Ord
PartialOrd和PartialEq差不多,PartialEq只判斷相等或不相等,PartialOrd在這個(gè)基礎(chǔ)上進(jìn)一步判斷是小于、小于等于、大于還是大于等于??梢钥吹?,它就是為排序功能準(zhǔn)備的。
PartialOrd被定義為 PartialEq的subtrait。它們?cè)陬愋蜕峡梢杂眠^程宏一起derive實(shí)現(xiàn)。
#[derive(PartialEq, PartialOrd)]
struct Point {
x: i32,
y: i32,
}
#[derive(PartialEq, PartialOrd)]
enum Stoplight {
Red,
Yellow,
Green,
}
8.5 Clone
這個(gè)trait給目標(biāo)類型提供了clone()方法用來完整地克隆實(shí)例。
#[stable(feature = "rust1", since = "1.0.0")]
#[lang = "clone"]
#[rustc_diagnostic_item = "Clone"]
#[rustc_trivial_field_reads]
pub trait Clone: Sized {
/// Returns a copy of the value.
///
/// # Examples
///
/// ```
/// # #![allow(noop_method_call)]
/// let hello = "Hello"; // &str implements Clone
///
/// assert_eq!("Hello", hello.clone());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use = "cloning is often expensive and is not expected to have side effects"]
// Clone::clone is special because the compiler generates MIR to implement it for some types.
// See InstanceKind::CloneShim.
#[lang = "clone_fn"]
fn clone(&self) -> Self;
/// Performs copy-assignment from `source`.
///
/// `a.clone_from(&b)` is equivalent to `a = b.clone()` in functionality,
/// but can be overridden to reuse the resources of `a` to avoid unnecessary
/// allocations.
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
fn clone_from(&mut self, source: &Self) {
*self = source.clone()
}
}
通過方法的簽名,可以看到方法使用的是實(shí)例的不可變引用。
fn clone(&self) -> Self;
比如:
#[derive(Clone)]
struct Point {
x: u32,
y: u32,
}
因?yàn)槊恳粋€(gè)字段(u32類型)都實(shí)現(xiàn)了Clone,所以通過derive,自動(dòng)為Point類型實(shí)現(xiàn)了Clone trait。實(shí)現(xiàn)后,Point的實(shí)例 point 使用 point.clone() 就可以把自己克隆一份了。
注意:clone() 是對(duì)象的深度拷貝,可能會(huì)有比較大的額外負(fù)載,但是就大多數(shù)情況來說其實(shí)還好。不要擔(dān)心在Rust中使用clone(),先把程序功能跑通最重要。Rust的代碼,性能一般都不會(huì)太差,畢竟起點(diǎn)很高。
8.6 Copy
#[rustc_unsafe_specialization_marker]
#[rustc_diagnostic_item = "Copy"]
pub trait Copy: Clone {
// Empty.
}
定義為Clone的subtrait,并且不包含任何內(nèi)容,僅僅是一個(gè)標(biāo)記(marker)。
Rust標(biāo)準(zhǔn)庫提供了Copy過程宏,可以讓我們自動(dòng)為目標(biāo)類型實(shí)現(xiàn)Copy trait。
8.7 ToOwned
ToOwned相當(dāng)于是Clone更寬泛的版本。ToOwned給類型提供了一個(gè) to_owned() 方法,可以將引用轉(zhuǎn)換為所有權(quán)實(shí)例。
let a: &str = "123456";
let s: String = a.to_owned();
8.8 Drop
Drop trait用于給類型做自定義垃圾清理(回收)。
trait Drop {
fn drop(&mut self);
}
實(shí)現(xiàn)了這個(gè)trait的類型的實(shí)例在走出作用域的時(shí)候,觸發(fā)調(diào)用drop()方法,這個(gè)調(diào)用發(fā)生在這個(gè)實(shí)例被銷毀之前。
#[derive(PartialEq, Debug, Clone)] // 注意這一句
struct Point {
x: i32,
y: i32,
}
impl Drop for Point {
fn drop(&mut self) {
println!("Dropping point ({},{})",self.x,self.y);
}
}
fn main() {
let p = Point { x: 1, y: 2 };
println!("{:?}", p);
}
輸出結(jié)果:

一般來說,我們不需要為自己的類型實(shí)現(xiàn)這個(gè)trait,除非遇到特殊情況,比如我們要調(diào)用外部的C庫函數(shù),然后在C那邊分配了資源,由C庫里的函數(shù)負(fù)責(zé)釋放,這個(gè)時(shí)候我們就要在Rust的包裝類型(對(duì)C庫中類型的包裝)上實(shí)現(xiàn)Drop,并調(diào)用那個(gè)C庫中釋放資源的函數(shù)。
8.9 From<T> 和 Into<T>
這兩個(gè) trait 用于類型轉(zhuǎn)換。
From<T> 可以把類型T轉(zhuǎn)為自己,而 Into<T> 可以把自己轉(zhuǎn)為類型T。
trait From<T> {
fn from(T) -> Self;
}
trait Into<T> {
fn into(self) -> T;
}
可以看到它們是互逆的trait。實(shí)際上,Rust只允許我們實(shí)現(xiàn) From<T>,因?yàn)閷?shí)現(xiàn)了From后,自動(dòng)就實(shí)現(xiàn)了Into,請(qǐng)看標(biāo)準(zhǔn)庫里的這個(gè)實(shí)現(xiàn)。
impl<T, U> Into<U> for T
where
U: From<T>,
{
fn into(self) -> U {
U::from(self)
}
}
8.10 TryFrom TryInto
TryFrom<T> 和 TryInto<T> 是 From<T> 和 Into<T> 的可失敗版本。如果你認(rèn)為轉(zhuǎn)換可能會(huì)出現(xiàn)失敗的情況,就選擇這兩個(gè)trait來實(shí)現(xiàn)。
trait TryFrom<T> {
type Error;
fn try_from(value: T) -> Result<Self, Self::Error>;
}
trait TryInto<T> {
type Error;
fn try_into(self) -> Result<T, Self::Error>;
}
可以看到,調(diào)用 try_from() 和 try_into() 后返回的是Result,你需要對(duì)Result進(jìn)行處理。
8.11 FromStr
從字符串類型轉(zhuǎn)換到自身。
trait FromStr {
type Err;
fn from_str(s: &str) -> Result<Self, Self::Err>;
}
比如字符串的 parse() 方法:
use std::str::FromStr;
fn example<T: FromStr>(s: &str) {
// 下面4種表達(dá)等價(jià)
let t: Result<T, _> = FromStr::from_str(s);
let t = T::from_str(s);
let t: Result<T, _> = s.parse();
let t = s.parse::<T>(); // 最常用的寫法
}
8.12 as_ref
trait AsRef<T> {
fn as_ref(&self) -> &T;
}
它把自身的引用轉(zhuǎn)換成目標(biāo)類型的引用。和Deref的區(qū)別是, deref() 是隱式調(diào)用的,而 as_ref() 需要你顯式地調(diào)用。所以代碼會(huì)更清晰,出錯(cuò)的機(jī)會(huì)也會(huì)更少。
AsRef<T> 可以讓函數(shù)參數(shù)中傳入的類型更加多樣化,不管是引用類型還是具有所有權(quán)的類型,都可以傳遞。比如;
// 使用 &str 作為參數(shù)可以接收下面兩種類型
// - &str
// - &String
fn takes_str(s: &str) {
// use &str
}
// 使用 AsRef<str> 作為參數(shù)可以接受下面三種類型
// - &str
// - &String
// - String
fn takes_asref_str<S: AsRef<str>>(s: S) {
let s: &str = s.as_ref();
// use &str
}
fn example(slice: &str, borrow: &String, owned: String) {
takes_str(slice);
takes_str(borrow);
takes_str(owned); // ?
takes_asref_str(slice);
takes_asref_str(borrow);
takes_asref_str(owned); // ?
}
在這個(gè)例子里,具有所有權(quán)的String字符串也可以直接傳入?yún)?shù)中了,相對(duì)于 &str 的參數(shù)類型表達(dá)更加擴(kuò)展了一步。
你可以把 Deref 看成是隱式化(或自動(dòng)化)+弱化版本的 AsRef<T>。
浙公網(wǎng)安備 33010602011771號(hào)