最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Rust常用特型之Drop特型

 更新時(shí)間:2024年03月14日 11:41:53   作者:AiMateZero  
本文主要介紹了Rust常用特型之Drop特型,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

Rust常用特型之Drop特型.md在Rust標(biāo)準(zhǔn)庫中,存在很多常用的工具類特型,它們能幫助我們寫出更具有Rust風(fēng)格的代碼。

今天,我們主要學(xué)習(xí)Drop特型。

(注:本文更多的是對(duì)《Programing Rust 2nd Edition》的自己翻譯和理解,并不是原創(chuàng))

一、什么是Drop

當(dāng)一個(gè)值不再擁有owner時(shí)(在Rust中每個(gè)值都有一個(gè)owner,并且最多只有一個(gè)owner),我們說Rust釋放/清理(Drop)了該值。釋放一個(gè)值通常意味著也需要一并釋放它占用的其它資源,例如堆存儲(chǔ)。釋放可以發(fā)生在多種場(chǎng)合:例如變量超出作用域,表達(dá)式語句的結(jié)尾,截?cái)嘁粋€(gè)向量并移除末尾的值等。

接下來的內(nèi)容中,清理和釋放表達(dá)的是同一個(gè)含義,均為drop的意思。

通常情況下,Rust會(huì)自動(dòng)為你清理值。例如如下代碼:

struct Appellation {
  name: String,
  nicknames: Vec<String>
}

這里我們來復(fù)習(xí)一下Vec<T>的有關(guān)知識(shí)。

一個(gè)Vec<T>由三個(gè)值構(gòu)成, 第一個(gè)值是指針,它指向在堆上為元素分配的緩沖區(qū)。 該緩沖區(qū)由Vec<T>本身擁有。第二值是緩沖區(qū)的容量Cap。第三個(gè)值是當(dāng)前元素的個(gè)數(shù)length。它是一個(gè)胖指針。當(dāng)緩沖區(qū)的大小達(dá)到它的容量時(shí),再增加元素會(huì)重新分配一個(gè)更大的緩沖區(qū),并將原來的元素復(fù)制過去,同時(shí)更新向量的指針,容量和長度值,最后釋放舊的緩沖區(qū)。

一個(gè)Appellation對(duì)象即包含了堆上的字符串內(nèi)容(對(duì)應(yīng)的name字段),又包含了堆上的向量元素緩沖區(qū)(對(duì)應(yīng)nicknames字段)。當(dāng)這個(gè)對(duì)象釋放時(shí),Rust會(huì)小心清理所有資源,并不需要你自己做任何處理。然而,如果你愿意,你也可以通過實(shí)現(xiàn)std::ops::Drop特型來自定義你的類型的清理方式這里為什么有個(gè)你的類型呢?因?yàn)镽ust不允許特型和類型都是外部的,必須有一個(gè)是本地的。此時(shí)Drop特型已經(jīng)是外來的(相對(duì)于你的代碼),因此類型必須是本地定義的。

Drop特型的定義為:

trait Drop {
  fn drop(&mut self);
}

個(gè)人理解,未必正確

我們可以看到,該特型僅有一個(gè)drop函數(shù),注意它的參數(shù)類型是&mut,因?yàn)槲覀円鱿嚓P(guān)清理工作,因此必須是可變的。如果參數(shù)是mut self會(huì)怎么樣?那么相當(dāng)于值轉(zhuǎn)移到本函數(shù)中了,在本函數(shù)處理完畢后該值的owner就不存在了,此時(shí)又到了調(diào)用drop的場(chǎng)景,從而形成無限循環(huán),所以參數(shù)類型必定為&mut

二、Drop特型的實(shí)現(xiàn)

當(dāng)一個(gè)值被清理時(shí),如果它實(shí)現(xiàn)了Drop特型,那么Rust會(huì)自動(dòng)調(diào)用它的drop方法。該調(diào)用發(fā)生在清理它的內(nèi)部元素或者字段之前。這說明用戶自定義的drop函數(shù)有第一優(yōu)先權(quán)。當(dāng)然這種隱匿調(diào)用也是調(diào)用drop函數(shù)的唯一方式,如果你手動(dòng)調(diào)用它,那么Rust會(huì)標(biāo)記為一個(gè)錯(cuò)誤。

這里也印證了上面提到的drop函數(shù)的參數(shù)類型&mut,因?yàn)榘l(fā)生在清理它的內(nèi)部元素之前,所以該值在此時(shí)必須保留,所以不能是mut self。也正因?yàn)槿绱?,這個(gè)值一定是初始化過的(應(yīng)該是變量初始化過)。

上面Appellation類型的一個(gè)示例Drop實(shí)現(xiàn)代碼為:

impl Drop for Appellation {
  fn drop(&mut self) {
    print!("Dropping {}", self.name);
    if !self.nicknames.is_empty() {
    	print!(" (AKA {})", self.nicknames.join(", "));
    }
    println!("");
  }
}

假定實(shí)現(xiàn)為上述代碼,那么我們可以接下來寫一段測(cè)試代碼:

{
  let mut a = Appellation {
    name: "Zeus".to_string(),
    nicknames: vec!["cloud collector".to_string(),
    "king of the gods".to_string()]
  };
  println!("before assignment");
  a = Appellation { name: "Hera".to_string(), nicknames: vec![]};
  println!("at end of block");
}

那么運(yùn)行得到的結(jié)果是什么呢?我們一行一行來分析代碼:

  • 1-6行,定義了一個(gè)類型為 Appellation 的mut變量a ,它的值在定義時(shí)已經(jīng)初始化了
  • 第7行,打印開始重新賦值信息before assignment并換行。
  • 第8行,將a重新賦值,此時(shí)a原來的值被拋棄了,沒有owner了,因此符合清理的條件,Rust會(huì)自動(dòng)對(duì)其進(jìn)行清理,在該值上調(diào)用drop函數(shù)
  • drop函數(shù)首先打印值的name,這里應(yīng)該是Dropping Zeus。注意這里是print!,未換行。
  • 接下來,因?yàn)?code>nicknames不為空,將它的元素使用,連接起來,所以應(yīng)該為 (AKA cloud collector,king of the gods)。注意這里是print!,未換行,因此是接在Dropping Zeus之后。
  • 接下來println!("");目的是產(chǎn)生換行。
  • drop函數(shù)調(diào)用完畢,接下來回到示例代碼第9行,打印at end of block。
  • 第10行,示例代碼結(jié)束,變量a超過作用域,在此釋放,也會(huì)調(diào)用其drop函數(shù)。
  • 再次回到drop函數(shù),打印對(duì)象名稱,此時(shí)應(yīng)該為Dropping Hera。
  • 因?yàn)榈诙€(gè)Appellation值的nicknames字段為空向量,所以不再打印AKA相關(guān)。
  • 再次換行。

最終輸出結(jié)果為:

before assignment
Dropping Zeus (AKA cloud collector, king of the gods)
at end of block
Dropping Hera

上面的代碼中,類型為Appellation的變量a前后有兩個(gè)不同的值,因此觸發(fā)了兩次清理。第一次清理發(fā)生在重新賦值時(shí),此時(shí)第一個(gè)值被拋棄,變成了無owner,所以觸發(fā)清理。第二次發(fā)生在代碼塊結(jié)束 ,此時(shí)a超出作用域,也觸發(fā)清理。

可以看到,我們的清理并沒有清除掉內(nèi)部元素占用的資源,這是Rust會(huì)在接下來自動(dòng)處理的,我們的工作主要是作一些額外的處理。

針對(duì)這個(gè)問題,書中已經(jīng)給了明確答案。Rust自動(dòng)清理內(nèi)部元素,而內(nèi)部元素也會(huì)自動(dòng)清理自己。例如Vec類型也實(shí)現(xiàn)了Drop特型,它會(huì)清理掉它的內(nèi)部元素并釋放它占用的堆上的緩沖區(qū)。字符串內(nèi)部使用Vec<u8>來保存它的文本,因此字符串并不需要自己實(shí)現(xiàn)Drop特型(Vec<T>實(shí)現(xiàn)了就可以),向量本身來處理這些字符的釋放。相同的原則應(yīng)用于Appellation值,向量的Drop實(shí)現(xiàn)會(huì)自動(dòng)釋放它的元素。對(duì)于 Appellation值本身,它也有一個(gè)owner,它可以是本地臨時(shí)變量或者某些數(shù)據(jù)結(jié)構(gòu),這個(gè)變量對(duì)釋放它負(fù)責(zé)。

注意:

當(dāng)一個(gè)變量的值被移走時(shí),該變量就是未初始化的,因此在超過作用域時(shí)并不會(huì)觸發(fā)drop,沒有值需要清理。切記,清理的是值不是變量。

下面的一段代碼:

let p;
  {
  let q = Appellation { name: "Cardamine hirsuta".to_string(),
  			nicknames: vec!["shotweed".to_string(),"bittercress".to_string()] };
  if complicated_condition() {
  	p = q;
  }
}
println!("Sproing! What was that?");

根據(jù)complicated_condition返回值的不同,p或者q其中的一個(gè)在代碼結(jié)束時(shí)會(huì)擁有這個(gè)Appellation值,另一個(gè)變量是未初始化。這也決定了他們是在最后的println!之前還是之后drop(這是因?yàn)閝的作用域在println!之前結(jié)束而p的作用域在這之后結(jié)束)。雖然在Rust中一個(gè)值可以從一個(gè)變量移到另一個(gè)變量,但是只會(huì)清理一次。

通常情況下,你不需要給自己定義的類型實(shí)現(xiàn)Drop特型,除非它擁有了Rust所不能自動(dòng)處理的資源。例如,在Unix系統(tǒng)中,Rust標(biāo)準(zhǔn)為使用如下的內(nèi)部結(jié)構(gòu)來代表操作系統(tǒng)文件描述:

struct FileDesc {
  fd: c_int,
}

其中fd字段代表的文件描述數(shù)字在程序結(jié)束的時(shí)候應(yīng)該關(guān)掉。標(biāo)準(zhǔn)庫因此為之實(shí)現(xiàn)了Drop特型來關(guān)掉它。

impl Drop for FileDesc {
  fn drop(&mut self) {
    let _ = unsafe { libc::close(self.fd) };
  }
}

這里,libc::close是C語言庫的close函數(shù)的Rust名字,Rust只能在unsafe代碼塊中調(diào)用C語言的函數(shù)。

知識(shí)點(diǎn):

如果一個(gè)類型實(shí)現(xiàn)了Drop特型,那么它不能再實(shí)現(xiàn)Copy特型。如果一個(gè)類型是Copy類型,那么意味著簡(jiǎn)單的字節(jié)復(fù)制就夠了,這樣可能會(huì)導(dǎo)致兩個(gè)變量會(huì)擁有同一塊數(shù)據(jù)。但是如果兩個(gè)變量都面臨清理時(shí),相同的數(shù)據(jù)就會(huì)清理兩次,這是一個(gè)錯(cuò)誤。就好像上面的FileDesc例子,如果它實(shí)現(xiàn)了Copy特型,那么另一個(gè)變量也會(huì)關(guān)閉相同的fd數(shù)字,顯然這是一個(gè)錯(cuò)誤。

進(jìn)一步思考,如果把Copy換成Clone呢?經(jīng)過測(cè)試是沒有問題的。

use std::ops::Drop;
// A unit struct without resources
#[derive(Debug, Clone)]
struct Unit;

impl Drop for Unit {
    fn drop(&mut self) {
        println!("in drop");
    }
}

fn main() {
    let a = Unit;
    let b = a.clone();
    println!("over:{:?}",b);
}

運(yùn)行結(jié)果為:

over:Unit
in drop
in drop

有人說那如果把FileDesc設(shè)計(jì)為實(shí)現(xiàn)Clone特型不一樣么?其實(shí)還真不一樣,因?yàn)?code>fd字段的排它性,所以把它設(shè)計(jì)為Clone是錯(cuò)誤的。只有可以復(fù)制的資源才能設(shè)計(jì)為實(shí)現(xiàn)Clone特型,這個(gè)問題其實(shí)是Clone特型的設(shè)計(jì)問題了,而不是Drop特型的問題。

有人說如果兩個(gè)變量都包含對(duì)同一塊數(shù)據(jù)的引用,那么是不是清理兩次呢?顯然不是,引用不擁有值,不會(huì)觸發(fā)清理。

標(biāo)準(zhǔn)前置還包含了一個(gè)drip函數(shù)用來清理一個(gè)值,但是它的定義相當(dāng)魔幻:

fn drop<T>(_x: T) { }

從代碼中可以看出,它接收一個(gè)值并且獲得了該值的owner。在函數(shù)結(jié)束時(shí)_x超出了作用域而會(huì)被Rust正常的清理掉。這里只是提供了一個(gè)便利功能,并不是手動(dòng)調(diào)用值的drop函數(shù)。

到此這篇關(guān)于Rust常用特型之Drop特型的文章就介紹到這了,更多相關(guān)Rust Drop特型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一文帶你掌握Rust中的字符串與切片

    一文帶你掌握Rust中的字符串與切片

    字符串與切片是所有新手遇到的第一個(gè)門檻,不同于?Java、Python?等語言對(duì)字符串的高度封裝,Rust?的字符串與切片深度綁定了所有權(quán)、借用、生命周期與?UTF-8?編碼,下面小編就和大家詳細(xì)介紹一下吧
    2026-03-03
  • Rust語言之結(jié)構(gòu)體和枚舉的用途與高級(jí)功能詳解

    Rust語言之結(jié)構(gòu)體和枚舉的用途與高級(jí)功能詳解

    Rust 是一門注重安全性和性能的現(xiàn)代編程語言,其中結(jié)構(gòu)體和枚舉是其強(qiáng)大的數(shù)據(jù)類型之一,了解結(jié)構(gòu)體和枚舉的概念及其高級(jí)功能,將使你能夠更加靈活和高效地處理數(shù)據(jù),本文將深入探討 Rust 中的結(jié)構(gòu)體和枚舉,并介紹它們的用途和高級(jí)功能
    2023-10-10
  • Rust動(dòng)態(tài)調(diào)用字符串定義的Rhai函數(shù)方式

    Rust動(dòng)態(tài)調(diào)用字符串定義的Rhai函數(shù)方式

    Rust中使用Rhai動(dòng)態(tài)調(diào)用字符串定義的函數(shù),通過eval_expression_with_scope實(shí)現(xiàn),但參數(shù)傳遞和函數(shù)名處理有局限性,使用FnCall功能更健壯,但更復(fù)雜,總結(jié)提供了更通用的方法,但需要處理更多錯(cuò)誤情況
    2025-02-02
  • 利用Rust編寫一個(gè)簡(jiǎn)單的字符串時(shí)鐘

    利用Rust編寫一個(gè)簡(jiǎn)單的字符串時(shí)鐘

    這篇文章主要為大家詳細(xì)介紹了一個(gè)用rust寫的一個(gè)簡(jiǎn)單的練手的demo,一個(gè)字符串時(shí)鐘,在終端用字符串方式顯示當(dāng)前時(shí)間,感興趣的小伙伴可以了解一下
    2022-12-12
  • 解析rust中的struct

    解析rust中的struct

    自定義的數(shù)據(jù)類型,為相關(guān)聯(lián)的值命名,打包成有意義的組合,類似python的dict,但是賦值的時(shí)候可以不按順序,本文給大家介紹下rust中的struct知識(shí),感興趣的朋友一起看看吧
    2022-10-10
  • Rust異步Web框架Axum的深入原理解析與高級(jí)用法

    Rust異步Web框架Axum的深入原理解析與高級(jí)用法

    Axum是一個(gè)功能強(qiáng)大、簡(jiǎn)單易用的異步Web框架,基于Tokio異步運(yùn)行時(shí),具有高度模塊化的架構(gòu)和類型安全的特點(diǎn),通過本文的介紹,我們學(xué)習(xí)了Axum框架的架構(gòu)與核心組件、路由系統(tǒng)、高級(jí)功能、性能優(yōu)化方法、實(shí)戰(zhàn)項(xiàng)目的應(yīng)用以及常見問題的解決方案
    2026-03-03
  • Rust中的Iterator和IntoIterator介紹及應(yīng)用小結(jié)

    Rust中的Iterator和IntoIterator介紹及應(yīng)用小結(jié)

    Iterator即迭代器,它可以用于對(duì)數(shù)據(jù)結(jié)構(gòu)進(jìn)行迭代,被迭代的數(shù)據(jù)結(jié)構(gòu)是可迭代的(iterable),所謂的可迭代就是這個(gè)數(shù)據(jù)結(jié)構(gòu)有返回迭代器的方法,這篇文章主要介紹了Rust中的Iterator和IntoIterator介紹及應(yīng)用,需要的朋友可以參考下
    2023-07-07
  • Rust使用Sqlx連接Mysql的實(shí)現(xiàn)

    Rust使用Sqlx連接Mysql的實(shí)現(xiàn)

    數(shù)據(jù)庫在編程中是一個(gè)很重要的環(huán)節(jié),本文主要介紹了Rust使用Sqlx連接Mysql的實(shí)現(xiàn),記錄rust如何操作數(shù)據(jù)庫并以mysql為主的做簡(jiǎn)單的使用說明,感興趣的可以了解一下
    2024-03-03
  • Rust語言中級(jí)教程之指針

    Rust語言中級(jí)教程之指針

    Rust中共有三種類型的指針,分別為引用,解引用,智能指針,這篇文章主要介紹了Rust語言中級(jí)教程之指針,需要的朋友可以參考下
    2023-05-05
  • 深入了解Rust中trait的使用

    深入了解Rust中trait的使用

    先前我們提到過?trait,那么Rust中的trait?是啥呢?本文將通過一些示例為大家詳細(xì)講講Rust中trait的使用,感興趣的小伙伴可以了解一下
    2022-11-11

最新評(píng)論

台北县| 高唐县| 金川县| 新津县| 石城县| 溧阳市| 长葛市| 黑河市| 台安县| 琼中| 武山县| 霍林郭勒市| 无锡市| 体育| 比如县| 潼南县| 义马市| 保德县| 柯坪县| 巴塘县| 玉溪市| 渝中区| 鹰潭市| 桃园县| 清流县| 宜兴市| 防城港市| 丹东市| 甘德县| 通化市| 江安县| 灵寿县| 信宜市| 格尔木市| 龙岩市| 台中县| 固阳县| 婺源县| 边坝县| 城固县| 和顺县|