Rust常用特型之ToOwned特型示例詳解
在Rust標(biāo)準(zhǔn)庫(kù)中,存在很多常用的工具類特型,它們能幫助我們寫出更具有Rust風(fēng)格的代碼。
ToOwned
這次我們來(lái)學(xué)一個(gè)和Borrow特型相關(guān)的特型,叫ToOwned類型??醋置嬉馑?code>Borrow是代表借出,而ToOwned代表去擁有它。
在Rust中,假定某類型實(shí)現(xiàn)了Clone特型,如果給你一個(gè)對(duì)它引用,那我們得到它指向內(nèi)容的備份的最常見方式是調(diào)用其clone()函數(shù)。但是如果你想克隆&str或者&[i32]時(shí)會(huì)發(fā)生什么呢?你的目的可能是想得到一個(gè)String或者Vec<i32>。但是根據(jù)Clone特型的定義,你無(wú)法得到它們。根據(jù)定義,對(duì)一個(gè)&T調(diào)用clone() 會(huì)返回一個(gè)T的值,也就是說(shuō)會(huì)返回str或者[i32]。而我們前面學(xué)習(xí)Sized特型的時(shí)候提到過(guò),str或者[i32] 是切片類型,無(wú)固定大小,是不能保存在變量中或者作為函數(shù)結(jié)果返回的。
std::borrow::ToOwned 特型提供了一個(gè)稍微寬松的方法將一引用轉(zhuǎn)換為可擁有的值。
trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
}上面的定義中,to_owned函數(shù)返回一個(gè)類型為Self::Owned的新鮮值,但是這個(gè)Owned可不是任意類型,它有個(gè)類型約束,也就是實(shí)現(xiàn)了Borrow<Self>. 也就是說(shuō)A能借出B/&B(實(shí)現(xiàn)了Borrow<B>),B才能擁有A.
例如,你可以從Vec<T>中借出一個(gè)&[T](這里的泛型U 為 [T] ),因此[T]可以實(shí)現(xiàn)ToOwned<Owned=Vec<T>>,只要T實(shí)現(xiàn)了Clone特型。這里為什么要對(duì)T限制呢?畢竟你要得到一個(gè)備份,如果一個(gè)T不能克隆,那么這個(gè)備份是無(wú)法實(shí)現(xiàn)的,因?yàn)樾枰亚衅脑貜?fù)制到新的向量中去。相似的,str實(shí)現(xiàn)了ToOwned<Owned=String>,因此我們可以調(diào)用&str的to_owned函數(shù)得到一個(gè)全新的字符串。Path也實(shí)現(xiàn)了ToOwned<Owned=PathBuf>,我們也可以從Path引用中得到一個(gè)全新的PathBuf值。
Humble Cow
Borrow和ToOwned聯(lián)動(dòng)可以實(shí)現(xiàn)一個(gè)很有意思的類型,Cow,注意它不是奶牛的意思,而是指 clone on write 我們趁熱來(lái)學(xué)習(xí)它。
充分利用 Rust 需要深思熟慮所有權(quán)問(wèn)題,例如某個(gè)函數(shù)是否應(yīng)該通過(guò)引用或值接收參數(shù)。通常你能確定使用其中的一種或者另一種(使用引用還是值),函數(shù)的參數(shù)類型代表了你的決定。但是存在這樣一些場(chǎng)景,你只有在運(yùn)行時(shí)才知道到底是需要借用還是引用,這時(shí),std::borrow::Cow類型就派上用場(chǎng)了,它的定義如下:
enum Cow<'a, B: ?Sized>
where B: ToOwned
{
Borrowed(&'a B),
Owned(<B as ToOwned>::Owned),
}這里可以看到,Cow是一個(gè)枚舉,有兩個(gè)變量,分別代表借用和擁有。其Borrow變量綁定了一個(gè)&B(這里先忽視生命周期標(biāo)記),這個(gè)B是個(gè)泛型,它的約束為B: ToOwned。它的目標(biāo)類型我們先假定為U,那么U 必定實(shí)現(xiàn)了Borrow<B>。
它的第二個(gè)枚舉變量為Owned,綁定了一個(gè)<B as ToOwned>::Owned的值,也就是U的值,所以Owned變量可以寫成Owned<U>。其中可以從U借出B,當(dāng)然,也可以從B擁有U的新值。
第一個(gè)枚舉變量,是綁定了&B,因此我們可以很方便的得到&B,第二個(gè)變量,是綁定了U,然而U又可以借出B,因此我們?nèi)匀豢梢院苋菀椎牡玫?code>&B( 通過(guò)U的borrow() 函數(shù))。兩個(gè)變量都可以方便的得到&B,因此它也實(shí)現(xiàn)了Deref特型,這樣你可以直接在Cow上調(diào)用B的相關(guān)函數(shù),而不管Cow是借用了B還是擁有了U。
你還可以在Cow類型的值上調(diào)用to_mut函數(shù)得到一個(gè)&mut B。 如果Cow變量剛好好Borrowed,則to_mut函數(shù)會(huì)先調(diào)用&B的to_owned方法得到它自己擁有的一個(gè)U的Copy,并對(duì)原來(lái)的變量進(jìn)行重新賦值,這樣就從Borrowed變量轉(zhuǎn)換成了Owned變量,然后再?gòu)男聯(lián)碛械腢的值中借出一個(gè)mut 引用。這里正是clone on write的含義所在(寫時(shí)clone).
我們來(lái)看一下這個(gè)Deref的實(shí)現(xiàn)代碼:
#[stable(feature = "rust1", since = "1.0.0")]
impl<B: ?Sized + ToOwned> Deref for Cow<'_, B>
where
B::Owned: Borrow<B>,
{
type Target = B;
fn deref(&self) -> &B {
match *self {
Borrowed(borrowed) => borrowed,
Owned(ref owned) => owned.borrow(),
}
}
}你代碼中我們可以看到,如果Cow是引用 ,直接將這個(gè)引用返回,如果是擁有的U值,則從U值借出,這里有一個(gè)細(xì)節(jié):
Owned(ref owned) => owned.borrow(), 因?yàn)槲覀兊膁eref函數(shù)接收參數(shù)為&self,因此我們無(wú)法在函數(shù)內(nèi)部消耗掉Cow本身,而match直接匹配時(shí)便會(huì)消耗這個(gè)值,因此為了阻止這種行為,添加了ref owned,代表這個(gè)owned只是獲取一個(gè)引用 ,因此這里的owned的類型其實(shí)為&U,所以直接調(diào)用其borrow()函數(shù)也就得到了一個(gè)&B. 注意borrow函數(shù)也是接收一個(gè)引用而非值作為參數(shù)。
to_mut函數(shù)的解釋為:
Acquires a mutable reference to the owned form of the data.
Clones the data if it is not already owned.
使用示例為:
use std::borrow::Cow;
let mut cow = Cow::Borrowed("foo");
cow.to_mut().make_ascii_uppercase();
assert_eq!(
cow,
Cow::Owned(String::from("FOO")) as Cow<'_, str>
);通過(guò)上面的示例我們可以看到,就算我們的cow是Borrowed變量,擁有一個(gè)共享的引用,到最后也變成了一個(gè)Owned變量。
我們來(lái)看一下實(shí)現(xiàn)過(guò)程:
#[stable(feature = "rust1", since = "1.0.0")]
pub fn to_mut(&mut self) -> &mut <B as ToOwned>::Owned {
match *self {
Borrowed(borrowed) => {
*self = Owned(borrowed.to_owned());
match *self {
Borrowed(..) => unreachable!(),
Owned(ref mut owned) => owned,
}
}
Owned(ref mut owned) => owned,
}
}這里 如果是Borrowed,則首先會(huì)to_owned得到U的一個(gè)新值,然后再將self重新賦值為Owned,然后再重新對(duì)self進(jìn)行match操作,
此時(shí)已經(jīng)是一個(gè)Owned,所以直接借出了ref mut,注意因?yàn)閙atch操作會(huì)消耗值,所以這里的Owned(ref mut owned) => owned, 中加了ref 代表是一個(gè)引用,結(jié)合上例,我們就得到了一個(gè)&mut String。
相似的,Cow也實(shí)現(xiàn)了into_owned方法將引用轉(zhuǎn)換為一個(gè)擁有的值。如果必須,則可以將值的所有權(quán)轉(zhuǎn)移給調(diào)用者,在這個(gè)過(guò)程中Cow本身的值會(huì)被消耗掉。
Cow一個(gè)常見的用法是返回一個(gè)靜態(tài)的字符串文字值常量或者一個(gè)動(dòng)態(tài)的字符串。例如,假定你需要將一個(gè)枚舉類型轉(zhuǎn)換成一個(gè)消息,枚舉的大多數(shù)變量都可用于固定的字符串,但是有一些變量或者一些額外的信息,因此你可以返回一個(gè)Cow<'static str>。
use std::path::PathBuf;
use std::borrow::Cow;
fn describe(error: &Error) -> Cow<'static, str> {
match *error {
Error::OutOfMemory => "out of memory".into(),
Error::StackOverflow => "stack overflow".into(),
Error::MachineOnFire => "machine on fire".into(),
Error::Unfathomable => "machine bewildered".into(),
Error::FileNotFound(ref path) => {
format!("file not found: {}", path.display()).into()
}
}
}上面的代碼使用了Cow的Into特型實(shí)現(xiàn)來(lái)構(gòu)造值。這里其實(shí)是Cow的From實(shí)現(xiàn),然后相對(duì)應(yīng)的&str就有了Into實(shí)現(xiàn)。這個(gè)Match的絕大多數(shù)分支都返回一個(gè)靜態(tài)分配的文字串文本用于Cow::Borrowed綁定,只有最后一個(gè)分支返回一個(gè)String用于Owned變量綁定。
describe函數(shù)的調(diào)用者不用管返回的到底Cow的哪個(gè)變量,它只用簡(jiǎn)單的將返回值看成是&str就行了,例如:
println!("Disaster has struck: {}", describe(&error));
如果你需要一個(gè)擁有的值,調(diào)用into_owned函數(shù)就可,例如 (describe(&error).into_owned() 就返回一個(gè)String。
使用Cow可以讓describle函數(shù)和它的調(diào)用者直到在需要時(shí)才會(huì)分配內(nèi)存來(lái)保存新生成的字符串。
到此這篇關(guān)于Rust常用特型之ToOwned特型的文章就介紹到這了,更多相關(guān)Rust ToOwned特型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust?編程語(yǔ)言中的所有權(quán)ownership詳解
這篇文章主要介紹了Rust?編程語(yǔ)言中的所有權(quán)ownership詳解的相關(guān)資料,需要的朋友可以參考下2023-02-02
Rust 的 into_owned() 方法實(shí)例詳解
into_owned是Rust語(yǔ)言中std::borrow::Cow 枚舉的一個(gè)方法,into_owned確保了調(diào)用者獲得數(shù)據(jù)的獨(dú)立所有權(quán),無(wú)論Cow之前是引用還是已經(jīng)擁有數(shù)據(jù),本文給大家介紹Rust 的 into_owned() 方法,感興趣的的朋友跟隨小編一起看看吧2024-03-03
Rust?搭建一個(gè)小程序運(yùn)行環(huán)境的方法詳解
rust是一門比較新的編程語(yǔ)言,2015年5月15日,Rust編程語(yǔ)言核心團(tuán)隊(duì)正式宣布發(fā)布Rust?1.0版本,本文給大家介紹Rust?搭建一個(gè)小程序運(yùn)行環(huán)境,以iOS?為例介紹開發(fā)環(huán)境的準(zhǔn)備,感興趣的朋友跟隨小編一起看看吧2022-05-05
Rust 中的 Packages 與 Crates模塊化構(gòu)建的基礎(chǔ)及開發(fā)流程
Rust中的Crate是編譯器處理的最小代碼單元,可以是二進(jìn)制或庫(kù),每個(gè)Crate由一個(gè)CrateRoot文件(通常是src/main.rs或src/lib.rs)定義,本文給大家介紹Rust 中的 Packages 與 Crates模塊化構(gòu)建的基礎(chǔ)及開發(fā)流程,感興趣的朋友一起看看吧2025-02-02

