国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Rust常用特型之Deref和DerefMut特型

這篇具有很好參考價值的文章主要介紹了Rust常用特型之Deref和DerefMut特型。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

在Rust標準庫中,存在很多常用的工具類特型,它們能幫助我們寫出更具有Rust風格的代碼。

你可以通過在你的類型上實現(xiàn)std::ops::Derefstd::ops::DerefMut特型來自定義解引用操作例如*操作符和.操作符的行為。像Box<T>Rc<T>實現(xiàn)了這兩個特型,所以他們的行為就像Rust內(nèi)置指針類型一樣。例如,如果你有一個Box<Complex>類型的值b,此時,*b 代表的是b指向的那個復數(shù)值。b.re代表該復數(shù)的實部.

如果對引用賦值或者借借出一個可變引用,那么Rust使用DerefMut特型。否則,只讀就足夠了,此時會使用Deref特型。

這兩個特型的定義類似如下:

trait Deref {
  type Target: ?Sized;
  fn deref(&self) -> &Self::Target;
}
trait DerefMut: Deref {
  fn deref_mut(&mut self) -> &mut Self::Target;
}

derefderef_mut函數(shù)都使用&self引用作為參數(shù)并且返回一個&Self::Target的引用。 Target是該類型所擁有的,包含的或者指向的一個具體變量類型(顯然,它不可能把它不知道的東西借出去,例如另一個結(jié)構(gòu)體中的字段)。例如對Box<Complex>來說,這里的Target就是Complex。注意DerefMut拓展了Deref,這時顯而易見的,如果你能解引用并修改它,那么你肯定能借出一個共享的引用而不修改。因為函數(shù)返回的引用的生命周期和&Self相同(生命周期三原則),只要函數(shù)返回的引用一直存在,那么Self本身就一直被借用。其實這里說的是只要是&Self有效,那么函數(shù)得到的引用就一直有效。

DerefDerefMut特型同時還扮演了其它角色。由于deref函數(shù)拿走了一個&Self引用但是返回了一個&Self::Target引用,Rust可以使用它來進行自動引用轉(zhuǎn)換,將&Self轉(zhuǎn)換成&Self::Target。換句話說,如果插入一個deref函數(shù)能消除類型不匹配的語法錯誤,Rust會自動為你插入。DerefMut也可以作相應的轉(zhuǎn)換,只不過是可變引用。這個功能叫著deref coericoins(強制解引用),一個類型被強制表現(xiàn)為另一種類型。

盡管你也可以自己寫一個類似強制轉(zhuǎn)引用的功能,但是它們很方便:

  • 如果你有一個Rc<String>類型的值r,你想在它上面應用String::find函數(shù),你可以簡單的寫成r.find(?),而不是(*r).find(?)。這是因為這個find函數(shù)調(diào)用隱式的借用r, 而Rc<T>實現(xiàn)了Deref<Target=T>,因此&Rc<String> 可以被解引用為&String.其實這里就是智能指針的應用(可以把智能指針當成普通內(nèi)置指針使用)

  • 你可以在String上使用split_at函數(shù),雖然該函數(shù)是定義在str字符串切片類型上的,因為String實現(xiàn)了Deref<Target=str>。String并不需要重新實現(xiàn)split_at函數(shù),因為你可以從&String強制解引用得到&str。 這里其實是Rust中的一個便利性,我們可以在非常底層的數(shù)據(jù)結(jié)構(gòu)上定義函數(shù),然后其它結(jié)構(gòu)可以直接使用。最常見的就是Vec<T>,其絕大部分函數(shù)是定義在切片[T]上的,但是因為強制解引用,你可以非常簡單的直接在向量上使用這些函數(shù)。我們看一下代碼直觀就更明顯:

    impl str {
        pub const fn len(&self) -> usize {
            self.as_bytes().len()
        }
      	
      	pub const fn is_empty(&self) -> bool {
            self.len() == 0
        }
    }
    

    我們可以看到,len及is_empty函數(shù)都是定義在str上的,其參數(shù)為&self,因此調(diào)用發(fā)生時,其實相當于是(&str).len。雖然我們這里是(&String).len(),但是由于自動解引用的存在,Rust會為我們自動插入deref函數(shù),將&String轉(zhuǎn)換成&str。

  • 如果你有一個字節(jié)向量v(類型為Vec<u8>),而你又想將它傳遞為參數(shù)為字節(jié)切片&[u8]的函數(shù),你可以直接傳遞&v即可。因為Vec<T>實現(xiàn)了Deref<Target=[T]>。這里和我上面剛說的定義在切片上的函數(shù)還有些不同,那個是解引用操作的,這里是函數(shù)參數(shù)傳遞,場景不一樣。

綜上所述,強制解引用主要應用在三個場合:

  1. 智能指針,讓智能指針變得和內(nèi)置指針一樣
  2. 共用函數(shù)定義,在底層結(jié)構(gòu)上定義函數(shù)從而讓其它類型直接使用。
  3. 函數(shù)多態(tài),例如函數(shù)參數(shù)為&[T],那么任何實現(xiàn)了Deref<Target=[T]>的類型都可以直接將引用傳遞給參數(shù)。

如果需要,Rust會執(zhí)行多重鏈式強制解引用 。例如你在Rc<String>上調(diào)用split_at函數(shù),首先,將它&Rc<String>解引用為&String,接下來再將&String解引用為&str,這正是split_at函數(shù)的擁有類型。

例如,如果你的類型如下所示:

struct Selector<T> {
  elements: Vec<T>,
  current: usize
}

雖然這里的類型涉及到了泛型,但相當簡單。你的結(jié)構(gòu)體包含了一個類型T的向量和一個記錄當前位置的索引(current)。而這個結(jié)構(gòu)體的功能就是實現(xiàn)一個指向當前元素的指針行為。

為了實現(xiàn)這個需求,Selector類型需要實現(xiàn)DerefDerefMut特型。

use std::ops::{Deref, DerefMut};

impl<T> Deref for Selector<T> {
  type Target = T;
  fn deref(&self) -> &T {
    &self.elements[self.current]
  }
}

impl<T> DerefMut for Selector<T> {
  fn deref_mut(&mut self) -> &mut T {
    &mut self.elements[self.current]
  }
}

代碼相當簡單了,我們就不說了。那怎么應用它呢?

let mut s = Selector { elements: vec!['x', 'y', 'z'],current: 2 };
*s = 'w';
assert_eq!(s.elements, ['x', 'y', 'w']);

這里s定義為mut的,這是因為我們要借用一個&mut T并修改它的值。

DerefDerefMut特型被設計用來實現(xiàn)實現(xiàn)智能指針類型,例如Box,Rc,Arc等,它還用于某些特定類型。這些類型擁有其它類型的值,但是需要頻繁通過引用來使用它內(nèi)部擁有的其它類型的值。例如Vec<T>String分別擁有[T]str。當你只有讓Target的方法自動出現(xiàn)在你的類型上這么一個目的時(正如c++中基類的方法自動出現(xiàn)在子類中),你不應該為它設計實現(xiàn)DerefDerefMut特型。它有可能并是總是像你期望的那樣工作,并且濫用也容易導致困惑。

具體困惑是什么呢?下面提到了一點。

自動解引用功能有時會隨著警告,這或許會讓你感到不解。因為Rust雖然使用自動解引用來解決類型沖突,但是并不包含類型參數(shù)的條件綁定。例如,下面的代碼工作的很好:

let s = Selector { elements: vec!["good", "bad", "ugly"],
current: 2 };
fn show_it(thing: &str) { println!("{}", thing); }
show_it(&s);

這是因為我們的Selector實現(xiàn)了Deref<Target=T>,而在上面使用場景的第三點函數(shù)多態(tài)時我們知道,傳入&s會自動執(zhí)行deref函數(shù)從而轉(zhuǎn)換成為&str .這里稍微有一點混亂。(這里是書中寫的,稍微有一點誤導)。

真實的事情是這樣的:

首先,s的類型為Selector<&str>(書中也是這么寫的),那么這里的T應該是&str而不是str。所以它實現(xiàn)的是Deref<Target=&str>而不是Deref<Target=str>(書中是這樣寫的,未知原因)。所以書中寫的相當于show_it(s.deref())這里deref函數(shù)返回的類型為&&str。但是傳入函數(shù)是沒有問題的,原因呢?估計是源碼中會再進行自動解引用 ,將&&T解成&T

經(jīng)過查詢相關資料,Rust Course《Rust語言圣經(jīng)》上是這樣說的,Rust會 引用 歸一化,也就是把&&&v當成&v。所以這里&&str實際上轉(zhuǎn)成了&str,因此傳入函數(shù)是沒有問題的。實際上,我的猜想是對的。看下面標準庫源碼,

impl<T: ?Sized> Deref for &T {
    type Target = T;

    fn deref(&self) -> &T {
        *self
    }
}

它為&T實現(xiàn)了Deref<Target=T>,雖然這里不擁有T,但是指向了T。(見前面特型定義)。所以&&T會自動解引用為&T。這么一來,s.deref()返回的&&str又被解引用成為了&str,最終滿足函數(shù)參數(shù)的類型。

所以這里書中講的稍微不對,漏了一點東西。而我們一定不放過,要有打破沙鍋問到底的精神。

接下來講,如果你把show_it改成一個泛型函數(shù),而類型參數(shù)T的限定為<T:Display>,那么問題來了,

use std::fmt::Display;
fn show_it_generic<T: Display>(thing: T) { println!("{}", thing);
}
show_it_generic(&s);

編譯器會告訴你Selector<&str>沒有實現(xiàn)std::fmt::Display,雖然&str實現(xiàn)了。

這里就是讓人困惑的地方,Selector<&str>不強制解引用為&str了么?怎么還會有錯誤?

實際上,你傳遞的參數(shù)類型為&Selector<&str>,而函數(shù)的參數(shù)為&T,所以T必須是Selector<&str>。其實這里是這樣的,T的類型是

Selector<&str>沒有錯的??赡苓@里需要涉及到Display的相關內(nèi)容。這里暫時略過。

Rust會檢查T是否滿足Display約束,因為在檢查約束時并不會應用強制解引用,顯然是無法通過檢查的。

解決辦法也很簡單,一是使用as操作符來手動解引用, show_it_generic(&s as &str);;另一種是按編輯器所建議,使用

show_it_generic(&*s);,這里因為有了*操作,所以會進行自動解引用操作得到&str,然后再加上前面的&就形成了&str,從而滿足條件T:Display.

這里有一點點不解的是,明明我們傳遞的是&s,為什么T的類型為Selector<&str>呢?

經(jīng)過研究,個人認為:其實T的類型仍然為&Selector<&str>,我們它把簡化為&U:Display 而,&U:Display的條件為 U:Display,因此給出的錯誤提示為Selector<&str>未實現(xiàn)Display,這里我們仔細看這句話就會明白:

help: the trait std::fmt::Display is not implemented for Selector<&str>, which is required by &Selector<&str>: std::fmt::Display

我們的函數(shù)參數(shù)檢查 后面半句,但是后面半句需要的條件為前面半句,因為所以書中講的還是有一些誤導(或者簡化)。

我們可以在函數(shù)體中增加如下代碼:println!("type is {}", std::any::type_name::<T>()),它用來打印類型名稱,當我們修改代碼使用&s as &str時,打印出來的類型為&str而不是str,這進一步印證了我的猜想。

這里還發(fā)現(xiàn)了上面表述的一點不對,如果我們使用&*s,打印出來的T的類型為&&str,奇怪吧。因為*號操作符相當于調(diào)用deref函數(shù)(不是普通引用的解引用時的*,那里的操作是獲取引用指向的值),所以*s得到的是&str,你不會得到str,因為str是無固定大小類型的,編譯通不過。*s再加一個&就得到了&&str。它是否滿足Display條件呢,Rust會移除&&直接檢查str,發(fā)現(xiàn)它是滿足的。標準庫有代碼如下:

impl Display for str {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        f.pad(self)
    }
}

注意,這里沒有使用歸一化,它沒有插入deref函數(shù)將&&str變成&str,因為它只是檢查條件。而Rust又自動實現(xiàn)了

impl<T> Display for &T where T:Display + ?Sized 。所以只要str實現(xiàn)了Display,不管前面加多少個&&都是實現(xiàn)了的。這也正是前面 the trait std::fmt::Display is not implemented for Selector<&str>, which is required by &Selector<&str>: std::fmt::Display 的原因,我們可以直接略去前面所有的&&而直接檢查T是否實現(xiàn)了。那么有沒有&T實現(xiàn)了Display而T沒有實現(xiàn)的呢?當然沒有,絕對不可能,看上面的實現(xiàn):``impl Display for &T where T:Display + ?Sized ,很顯然。文章來源地址http://www.zghlxwxcb.cn/news/detail-858473.html

到了這里,關于Rust常用特型之Deref和DerefMut特型的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • Rust 常用集合(上)

    Rust 常用集合(上)

    目錄 1、使用 Vector 儲存列表 1.1?新建 vector 1.2?更新 vector 1.3?讀取 vector 的元素 1.4?遍歷 vector 中的元素 1.5?使用枚舉來儲存多種類型 1.6?丟棄 vector 時也會丟棄其所有元素 2、使用字符串儲存 UTF-8 編碼的文本 2.1?什么是字符串? 2.2?新建字符串 2.3?更新字符串 2.3.1?使用 pus

    2024年02月03日
    瀏覽(14)
  • Rust常用加密算法

    Rust常用加密算法

    哈希運算(以Sha256為例) main.rs : Cargo.toml : 輸出為: 6d65924d8e0580b9ac04d13da91c74c3ae28b08b4be4634ae06e647f42a88913 可以在線比對驗證一下 驗證數(shù)據(jù)完整性(使用HMAC) MAC(Message Authentication Code,消息認證碼算法)是含有密鑰散列函數(shù)算法,兼容了MD和SHA算法的特性,并在此基礎上加上了密鑰。

    2024年02月11日
    瀏覽(23)
  • Rust vs Go:常用語法對比(八)

    Rust vs Go:常用語法對比(八)

    題目來自 Golang vs. Rust: Which Programming Language To Choose in 2023? [1] 141. Iterate in sequence over two lists Iterate in sequence over the elements of the list items1 then items2. For each iteration print the element. 依次迭代兩個列表 依次迭代列表項1和項2的元素。每次迭代打印元素。 1 2 3 a b c 142. Hexadecimal digits of

    2024年02月15日
    瀏覽(25)
  • Rust vs Go:常用語法對比(二)

    21. Swap values 交換變量a和b的值 輸出 a: 10, b: 3 or 輸出 22. Convert string to integer 將字符串轉(zhuǎn)換為整型 or 輸出 or 輸出 or 輸出 23. Convert real number to string with 2 decimal places Given a real number x, create its string representation s with 2 decimal digits following the dot. 給定一個實數(shù),小數(shù)點后保留兩位小數(shù)

    2024年02月16日
    瀏覽(27)
  • Rust vs Go:常用語法對比(三)

    Rust vs Go:常用語法對比(三)

    題圖來自 When to use Rust and when to use Go [1] 41. Reverse a string 反轉(zhuǎn)字符串 輸出 or 輸出 ? roma tis r?lod müspi mérol 42. Continue outer loop Print each item v of list a which in not contained in list b. For this, write an outer loop to iterate on a and an inner loop to iterate on b. 打印列表a中不包含在列表b中的每個項目

    2024年02月16日
    瀏覽(22)
  • Rust vs Go:常用語法對比(九)

    Rust vs Go:常用語法對比(九)

    題圖來自 Golang vs Rust - The Race to Better and Ultimate Programming Language 161. Multiply all the elements of a list Multiply all the elements of the list elements by a constant c 將list中的每個元素都乘以一個數(shù) [4.0, 7.0, 8.0] 162. Execute procedures depending on options execute bat if b is a program option and fox if f is a program optio

    2024年02月15日
    瀏覽(26)
  • Rust vs Go:常用語法對比(四)

    Rust vs Go:常用語法對比(四)

    題圖來自 Go vs. Rust performance comparison: The basics 61. Get current date 獲取當前時間 Now is 2009-11-10 23:00:00 +0000 UTC m=+0.000000001 or SystemTime { tv_sec: 1526318418, tv_nsec: 699329521 } 62. Find substring position 字符串查找 查找子字符串位置 i is the byte index of y in x, not the character (rune) index. i will be -1 if y i

    2024年02月16日
    瀏覽(19)
  • Rust vs Go:常用語法對比(六)

    Rust vs Go:常用語法對比(六)

    題圖來自 [1] 101. Load from HTTP GET request into a string Make an HTTP request with method GET to URL u, then store the body of the response in string s. 發(fā)起http請求 res has type *http.Response. buffer has type []byte. It is idiomatic and strongly recommended to check errors at each step. GET response: 200 Hello Inigo Montoya or or 102. Load from H

    2024年02月15日
    瀏覽(24)
  • Rust vs Go:常用語法對比(七)

    Rust vs Go:常用語法對比(七)

    題圖來自 Go vs Rust: Which will be the top pick in programming? [1] 121. UDP listen and read Listen UDP traffic on port p and read 1024 bytes into buffer b. 聽端口p上的UDP流量,并將1024字節(jié)讀入緩沖區(qū)b。 122. Declare enumeration Create an enumerated type Suit with 4 possible values SPADES, HEARTS, DIAMONDS, CLUBS. 聲明枚舉值 Hearts

    2024年02月15日
    瀏覽(20)
  • Rust vs Go:常用語法對比(五)

    Rust vs Go:常用語法對比(五)

    題圖來自 Rust vs Go 2023 [1] 81. Round floating point number to integer Declare integer y and initialize it with the rounded value of floating point number x . Ties (when the fractional part of x is exactly .5) must be rounded up (to positive infinity). 按規(guī)則取整 2.71828 3 82. Count substring occurrences 統(tǒng)計子字符串出現(xiàn)次數(shù) 1 Disjoint ma

    2024年02月15日
    瀏覽(21)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包