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

Rust內(nèi)存示例深入講解

 更新時間:2026年02月04日 08:43:13   作者:Kiri霧  
內(nèi)存管理一直是編程中的一大挑戰(zhàn),尤其是在C和C++中,程序員需要手動處理內(nèi)存分配和釋放,這篇文章主要介紹了Rust內(nèi)存的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下

內(nèi)存

大多數(shù)應(yīng)用程序,不論服務(wù)器、區(qū)塊鏈、人工智能、游戲還是其他領(lǐng)域。都需要數(shù)據(jù)。因此理解內(nèi)存的復(fù)雜性相當(dāng)重要。

不同類型的內(nèi)存往往依賴于幾個因素:

  • 數(shù)據(jù)大小
  • 所有權(quán)
  • 生命周期
  • 可變性
  • 持久性

這些因素綜合起來,將幫助你做出明確的決定。

其中三個主要的內(nèi)存區(qū)域是棧、堆和靜態(tài)。你可以將數(shù)據(jù)放置在這些位置中的任何一個。有時,Rust會提供一些指引,比如將向量的元素放置在堆上。 然而主要還是你自己決定數(shù)據(jù)的位置。

應(yīng)該注意的是,Rust沒有正式的內(nèi)存管理模型(一套定義良好的規(guī)則和機制,用于自動管理內(nèi)存的分配,使用和釋放從而確保內(nèi)存高效、安全地使用)。但是Rust的特性(例如默認不可變性、智能指針、所有權(quán)和生命周期)形成了一種非正式的內(nèi)存管理模型。

每個線程都擁有一個棧,它是一種專用內(nèi)存。

  • 當(dāng)線程調(diào)用一個函數(shù)時,棧會增長。
  • 當(dāng)線程從函數(shù)返回時,棧會縮小。
    每個函數(shù)都有一個棧幀,它為函數(shù)保留內(nèi)存。棧幀中的內(nèi)存用于局部變量、參數(shù)、返回值和系統(tǒng)數(shù)據(jù)。這些數(shù)據(jù)會被系統(tǒng)自動釋放。
    棧的實現(xiàn)是一個先進后出(LIFO)隊列,類似于一碟盤子,新的盤子總放置在頂部,并按順序從頂部移除。這種方式意味著數(shù)據(jù)高效地存儲在連續(xù)的內(nèi)存中。棧具有可預(yù)測的行為,因此系統(tǒng)能有效地管理棧。
    對于Rust,除主線程(main線程)外,默認棧大小是2K字節(jié),當(dāng)生成一個線程時,
  • 你可以使用Builder類型和stack_size函數(shù)明確設(shè)置最小棧的大小。
  • 或者使用RUST_MIN_STACK環(huán)境變量更改默認棧大小。

然而上面兩種方法都沒有設(shè)置棧大小上限,因為棧是可增長的,會在需要時擴展,直到達到可用內(nèi)存容量的限制。
let語句可用于當(dāng)前棧幀內(nèi)的內(nèi)存中創(chuàng)建一個本地變量。

fn main() {
    let a = 1;
    let b = 2;
    let c = do_something();
    
    println!("{:p} + {}(i32) = {:p} + {}(i32) {:p}",&a, &b-&a, &b, &c-&b, &c);
}

fn do_something()->i32 {
    3
}

可以看到,本地變量a、b和c在棧上占據(jù)連續(xù)的內(nèi)存位置

即使在函數(shù)內(nèi)部,數(shù)據(jù)也可以被添加和從棧中移除。

fn main() {
    let a = 1;
    let b = 2;
    {
        let c = 3;
    }

    println!("{} + {} = {}", a, b, c); //報錯,因為c已經(jīng)被移除了
}

UnSized(?Sized)類型不能放在棧上。

fn do_something(a: Copy) {

}

因為Copy trait是一個UnSized的。編譯器阻止了不符合條件的參數(shù)被放置到棧上。
解決方法是結(jié)合dyn(dyn Copy)或impl(impl Dopy)關(guān)鍵字,這些關(guān)鍵字用具體類型替換了trait,它們是定長的。

注意事項

  • ??赡軙拇罅績?nèi)存,因此在棧上放置大量對象時要小心。
  • 另一個問題是遞歸函數(shù),不經(jīng)意的無限遞歸會迅速耗盡可用內(nèi)存。
  • 一些數(shù)據(jù)類型(比如向量和字符串)是智能指針,當(dāng)使用let語句聲明時,這些類型的值會在堆上分配。指向該值的指針存放在棧上
fn main() {
    let vp = vec![1,2,3,4];
    println!("{:?}",vp);
}

上述例子中,vp是一個變量,代表胖指針,被放在棧上,而值[1,2,3,4]被放置在堆上

靜態(tài)值

靜態(tài)值在應(yīng)用程序的生命周期內(nèi)是持久的。這是通過將靜態(tài)值存儲在二進制文件本身來實現(xiàn)的。這種方式使得這些值始終可用。這也意味著大量的靜態(tài)值會導(dǎo)致二進制文件膨脹,這可能會影響性能。此外,為了保證靜態(tài)安全,靜態(tài)值很少是可變的。

可以使用static關(guān)鍵字聲明靜態(tài)綁定。按照規(guī)范,靜態(tài)值的名稱應(yīng)該全大寫。此外,靜態(tài)值的類型不可被推斷,必須顯式聲明類型。

fn main() {
    static PI: f64 = 3.14;
    let r = 4.0;
    println!("面積: {}",PI*r*r);
}

與棧變量相比,靜態(tài)值的地址明確顯示出它們位于內(nèi)存中不同的區(qū)域。

fn main() {
    static A: i32 = 10;
    static B: i32 = 20;
    let a = 10;
    let b = 20;
    println!("Global: ptr_A {:p} ptr_B {:p}", &A, &B);
    println!("Stack: prt_a {:p} ptr_b {:p}", &a, &b);
}

堆是運行時可供應(yīng)用程序使用的進程內(nèi)存。這通常是應(yīng)用程序最大的可用內(nèi)存池,是放置大型對象的地方。在運行時,應(yīng)用程序會根據(jù)需要在堆上分配內(nèi)存。這通常被稱為動態(tài)內(nèi)存分配。當(dāng)不需要時,堆內(nèi)存可被釋放,返回可用池中。

堆內(nèi)存取自應(yīng)用程序的虛擬內(nèi)存。一個進程與設(shè)備上其他正在運行的進程共享物理內(nèi)存。因此一個應(yīng)用程序并不擁有計算機上的所有內(nèi)存。相反,應(yīng)用程序被分配了一個虛擬地址空間(虛擬內(nèi)存),然后操作系統(tǒng)將其映射到物理內(nèi)存。

在申請對內(nèi)存時,操作系統(tǒng)必須首先找到足夠的連續(xù)內(nèi)存以滿足要求,然后在該位置分配內(nèi)存,并返回一個指向該地址的指針。地位和分配內(nèi)存的過程可能會比較耗時。

  • 此外,堆可能會因為一系列不同大小的數(shù)據(jù)分配操作而變得碎片化。即使有足夠的內(nèi)可用存,但不是單一位置的整塊內(nèi)存,也可能導(dǎo)致內(nèi)存分配失敗。一些操作系統(tǒng)提供了系統(tǒng)API來對堆進行整理,以緩解該問題。

與棧不同,堆是進程內(nèi)所有線程都可以訪問的共享內(nèi)存。因此堆上的數(shù)據(jù)可能不是內(nèi)存安全的。但是我們可以用RwLock這樣的類型來管理共享內(nèi)存。

在Rust中,Box類型用于在堆上分配內(nèi)存。當(dāng)Box被釋放時,通常在當(dāng)前塊的末尾,釋放這個堆內(nèi)存。然而,如果Box的值一直沒得到釋放,就會導(dǎo)致內(nèi)存泄漏?;蛘?,可以使用drop關(guān)鍵字顯式釋放Box及相關(guān)內(nèi)存。

pub struct Box<T, A = Global>(_, _)
    where A: Allocator, T: ?Sized;

Box是泛型結(jié)構(gòu)體,它的類型參數(shù)是T,T是動態(tài)分配的類型(?Sized),類型參數(shù)A是對內(nèi)存分配器的引用,Global是默認的分配器件,用于在堆上分配內(nèi)存。如果需要,你可以使用自定義分配器。
可以使用new構(gòu)造函數(shù)創(chuàng)建一個Box

fn new(x: T)->Box<T, Global>

Box::new函數(shù)用于在堆上創(chuàng)建一個值,并返回一個Box值,而不是指向堆內(nèi)存的原始指針。
要訪問堆上的Box值,需要對Box進行解引用。然而這種解引用也不是必須的,有時會發(fā)生自動解引用。例如println!宏。

fn main() {
    let boxa = Box::new(10);
    let stackb=*boxa+1;
    println!("{} {}", boxa, stackb);
}

示例

fn main() {
    let boxa = Box::new(1);
    let boxb = Box::new(2);

    let c = 1;
    let d = 2;
    println!("boxa:{:p} boxb:{:p} &c{:p} &d:{:p}",&boxa, &boxb, &c, &d);
    
    let rawa = Box::into_raw(boxa);
    let rawb = Box::into_raw(boxb);

    println!("rawa:{:p} rawb{:p} &c:{:p} &d:{:p}", rawa, rawb, &c, &d);

    let boxc;
    let boxd;
    unsafe {
        boxc = Box::from_raw(rawa);
        boxd = Box::from_raw(rawb);
    }

    println!("boxc value:{}", *boxc);
    println!("boxd value:{}", *boxd);
}
  • Box本身位于棧上,即使引用了堆上的數(shù)據(jù)。
  • into_raw函數(shù)用于獲取Box值的原始指針(rawa和rawb)。原始指針直接指向堆內(nèi)存,并且是unsafe的。當(dāng)它被釋放時,堆內(nèi)存不會移除。
  • Box值與局部變量c和d位于內(nèi)存的不同區(qū)域。
  • 你還可以使用from_raw函數(shù)將原始指針重新放回Box中,此后,Box將恢復(fù)對堆上數(shù)據(jù)項的責(zé)任。必須將from_raw標(biāo)記為unsafe來調(diào)用。

你也可以將棧上的值移動到堆,其結(jié)果取決于值移動是復(fù)制語義還是移動語義。

例如,將String變?yōu)?code>Box<String>變量時,所有權(quán)被轉(zhuǎn)移到堆上,也就是說String智能指針本身被移動到堆上。

fn main() {
    let a = 10;
    let mut boxa = Box::new(a);
    *boxa += 1;
    println!("{} {}", a, *boxa);
}

內(nèi)部可變性

內(nèi)部可變性用一個場景來描述是:

你管理你個大型連鎖超市內(nèi)的一家雜貨店。在結(jié)賬時,顧客購物車里的商品會被合計并記錄到收據(jù)上。收據(jù)上的商店Id和交易Id是固定的,而總金額字段是可變的。

struct Transaction {
    storeid: i8,
    txid: i32,
    mut total: f64 //錯誤的,結(jié)構(gòu)體某單個字段無法聲明為可變的
}

結(jié)構(gòu)體單個字段無法聲明為可變,可變性是在結(jié)構(gòu)體級別上聲明的。

但是這會導(dǎo)致不恰當(dāng)?shù)母?/p>

#[derive(Debug)]
struct Transaction {
    storeid: i8,
    txid: i32,
    total: f64,
}

fn main() {
    let mut tx = Transaction {storeid: 0, txid: 0, total: 64.0};
    tx.storeid=101; // oops
    println!("{:?}", tx);
}

解決辦法是內(nèi)部可變性。

支持內(nèi)部可變性的類型是一種內(nèi)部值的包裝器,包裝器呈現(xiàn)了一種不可變的外觀,同時間接允許對其內(nèi)部值的修改。

Cell

Cell是一種支持內(nèi)部可變性的類型,它的類型參數(shù)是T,其中T描述了內(nèi)部值,Cell位于std::cell模塊中

Cell可以保證不變性,而內(nèi)部值可以使用方法修改

  • Cell::get 返回內(nèi)部值的副本
  • Cell::set 修改內(nèi)部值
fn get(&self)->T
fn set(&self, val: T)

可以使用Cell::new函數(shù)創(chuàng)建一個Cell

fn new(value: T)->Cell<T>

修改最開始的例子

use std::cell::Cell;

fn main() {
    let cell = Cell::new(0);
    let data = cell.get();
    cell.set(1);
    println!("cell:{} data:{}", cell.get(), data);
}
use std::cell::Cell;

#[derive(Debug)]
struct Transaction {
    storeid: i8,
    txid: i32,
    total: Cell<f64>,
}

fn main() {
    let item_prices = [11.21, 25.45, 30.5];
    let tx = Transaction {
        storeid: 100,
        txid: 203,
        total: Cell::new(0.0),
    };

    for prices in item_prices {
        let total = tx.total.get()+prices;
        tx.total.set(total);
    }
    
    println!("{:?}",tx);
    
}

Cell還有一個好處,比如下面的代碼,Rust不允許存在多個可變借用

fn main() {
    let mut a = 1;
    let ref1 = &a; 
    let ref2 = &a;

    let mut ref3 = &mut a;
    let mut ref4 = &mut a;
    *ref3=2;
    println!("{ref3}");
}

如果用Cell

use std::cell::Cell;

fn main() {
    let a = 10;
    let cell = Cell::new(a);
    let cell1 = &cell;
    let cell2 = &cell;
    cell1.set(11);
    cell2.set(12);
    println!("{}",cell.get());
}

我們可以通過多個不同的引用修改內(nèi)部值。
還有一些Cell的函數(shù)

  • replace: 用新值替換內(nèi)部值,然后返回被替換的舊值
  • swap: 交換兩個Cell的內(nèi)部值
  • take: 獲取內(nèi)部值并將其內(nèi)部替換為默認值

RefCell

RefCell也位于std::cell模塊中,與Cell不同的是,RefCell只提供對內(nèi)部值的引用,而不是副本。

  • RefCell::borrow 獲取不可變借用
  • RefCell::borrow_mut 獲取可變借用
fn borrow(&self)->Ref<'_, T>
fn borrow_mut(&self)->RefMut<'_, T>

可以用new函數(shù)創(chuàng)建一個ReCell

fn new(value: T)->RefCell<T>

示例

use std::cell::RefCell;

fn main() {
    let ref_cell = RefCell::new(0);
    *ref_cell.borrow_mut() += 10;
    println!("*ref_cell: {}",ref_cell.borrow());
}

對于RefCell,可變性規(guī)則(不允許存在多個可變借用)完全適用。然而這些規(guī)則是在運行時而不是編譯時強制執(zhí)行的。因此要格外小心不要違反這些規(guī)則,會出現(xiàn)panic

use std::cell::RefCell;

fn main() {
    let refcell = RefCell::new(0);
    let mut ref1 = refcell.borrow_mut();
    let mut ref2 = refcell.borrow_mut();

    *ref1 = 10;
    println!("{}",ref1);
}

示例二

```rust
use std::cell::RefCell;

fn main() {
    let refcell = RefCell::new(0);
    let ref1 = refcell.borrow();
    let mut ref2 = refcell.borrow_mut();

    println!("{}",ref1);
}

try_borrow函數(shù)是borrow函數(shù)的一個替代方案。該函數(shù)返回一個Result類型,當(dāng)已存在一個可變引用時,它不會引發(fā)panic.而是直接返回Result類型的Err。如果成功則返回Ok(reference)

use std::cell::RefCell;

fn main() {
    let refcell = RefCell::new(0);
    let ref1 = refcell.borrow();
    let ret = refcell.try_borrow_mut();
    match ret {
        Ok(value) => println!("Interior value: {}", value),
        Err(_) => println!("不可再聲明可變借用"),
    }
    
}

還是修改最開始的例子

use std::cell::RefCell;


#[derive(Debug)]
struct Transaction {
    storeid: i8,
    txid: i32,
    total: RefCell<f64>,
}

fn main() {
    let item_prices = [11.5, 20.5, 30.0, 40.3];
    let tx = Transaction{storeid: 100, txid: 203, total: RefCell::new(0.0)};

    for prices in item_prices {
        *tx.total.borrow_mut() += prices
    }

    println!("{:#?}", tx)
}

RefCell還有其他有用的方法

  • replace: 用另一個值替換內(nèi)部值,并返回當(dāng)前值
  • swap: 交換兩個RefCell的內(nèi)部值

OnceCell

與Cell和RefCell類似,區(qū)別是OnceCell只能修改一次內(nèi)部值。如果再次修改會發(fā)生錯誤??梢杂胣ew函數(shù)創(chuàng)建一個OnceCell.

  • set用于初始化內(nèi)部值
  • get返回內(nèi)部值,可以根據(jù)需要多次獲取內(nèi)部值
fn new()->OnceCell<T>
fn set(&selfm value, T)->Result<(), T>
fn get(&self)->Option<&T>

示例

use std::cell::OnceCell;

fn main() {
    let once = OnceCell::new();
    let mut result = Ok(());
    for i in 1..=3 {
        result = once.set(i);
        match result {
            Ok(_) => println!("Updated"),
            Err(_) => println!("Not undated")
        }
    }
    println!("{:?}", result);
}

OnceCell其他有用的函數(shù)

  • get_mut 獲取內(nèi)部值的可變引用
  • get_or_init 獲取內(nèi)部值,如果未初始化,則使用閉包初始化它
  • take 獲取內(nèi)部值然后將內(nèi)部設(shè)置為默認值

總結(jié) 

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

相關(guān)文章

  • Rust獲取命令行參數(shù)及IO操作方法

    Rust獲取命令行參數(shù)及IO操作方法

    Rust作為一門系統(tǒng)編程語言,提供了強大且安全的I/O操作支持,本文給大家介紹Rust獲取命令行參數(shù)以及IO操作,感興趣的朋友一起看看吧
    2025-06-06
  • Rust 數(shù)據(jù)類型詳解

    Rust 數(shù)據(jù)類型詳解

    本文介紹了Rust編程語言中的標(biāo)量類型和復(fù)合類型,標(biāo)量類型包括整數(shù)、浮點數(shù)、布爾和字符,而復(fù)合類型則包括元組和數(shù)組,標(biāo)量類型用于表示單個值,具有不同的表示和范圍,本文介紹的非常詳細,感興趣的朋友一起看看吧
    2025-01-01
  • 詳解Rust 修改源

    詳解Rust 修改源

    這篇文章主要介紹了Rust 修改源的相關(guān)知識,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2024-01-01
  • 利用Rust實現(xiàn)一個簡單的Ping應(yīng)用

    利用Rust實現(xiàn)一個簡單的Ping應(yīng)用

    這兩年Rust火的一塌糊涂,甚至都燒到了前端,再不學(xué)習(xí)怕是要落伍了。最近翻了翻文檔,寫了個簡單的Ping應(yīng)用練練手,感興趣的小伙伴可以了解一下
    2022-12-12
  • 詳解Rust調(diào)用tree-sitter支持自定義語言解析

    詳解Rust調(diào)用tree-sitter支持自定義語言解析

    使用Rust語言結(jié)合tree-sitter庫解析自定義語言需要定義語法、生成C解析器,并在Rust項目中集成,具體步驟包括創(chuàng)建grammar.js定義語法,使用tree-sitter-cli工具生成C解析器,以及在Rust項目中編寫代碼調(diào)用解析器,這一過程涉及到對tree-sitter的深入理解和Rust語言的應(yīng)用技巧
    2024-09-09
  • Rust中字符串類型String的46種常用方法分享

    Rust中字符串類型String的46種常用方法分享

    Rust主要有兩種類型的字符串:&str和String,本文主要為大家介紹的是String類型的字符串以及它常用的46種方法,感興趣的小伙伴可以了解一下
    2023-06-06
  • Rust 中解析 JSON的方法

    Rust 中解析 JSON的方法

    要開始在 Rust 中使用 JSON,您需要安裝一個可以輕松操作 JSON 的庫,目前可用的流行crate之一是 serde-json,在本文中,我們將討論如何在 Rust 中使用 JSON 解析庫,以及比較最流行的庫及其性能
    2024-03-03
  • Rust中的方法與關(guān)聯(lián)函數(shù)使用解讀

    Rust中的方法與關(guān)聯(lián)函數(shù)使用解讀

    在Rust中,方法是定義在特定類型(如struct)的impl塊中,第一個參數(shù)是self(可變或不可變),方法用于描述該類型實例的行為,而關(guān)聯(lián)函數(shù)則不包含self參數(shù),常用于構(gòu)造新實例或提供一些與實例無關(guān)的功能,Rust的自動引用和解引用特性使得方法調(diào)用更加簡潔
    2025-02-02
  • Rust安裝與環(huán)境配置超詳細教程(零基礎(chǔ))

    Rust安裝與環(huán)境配置超詳細教程(零基礎(chǔ))

    Rust是一門系統(tǒng)編程語言,專注于安全,尤其是并發(fā)安全,支持函數(shù)式和命令式以及泛型等編程范式的多范式語言,這篇文章主要介紹了Rust安裝與環(huán)境配置超詳細教程的相關(guān)資料,需要的朋友可以參考下
    2026-02-02
  • Rust 能夠取代 C 語言嗎

    Rust 能夠取代 C 語言嗎

    Rust 是 Mozilla 基金會的一個雄心勃勃的項目,號稱是 C 語言和 C++ 的繼任者,這篇文章主要介紹了Rust 能夠取代 C 語言嗎的相關(guān)知識,需要的朋友可以參考下
    2020-06-06

最新評論

德阳市| 勐海县| 木里| 资阳市| 沧州市| 信宜市| 玛多县| 安多县| 苍溪县| 紫金县| 南汇区| 灵丘县| 盐亭县| 颍上县| 兴化市| 乐亭县| 吉隆县| 双桥区| 新巴尔虎右旗| 固始县| 天峨县| 团风县| 获嘉县| 襄城县| 丘北县| 休宁县| 石嘴山市| 麻栗坡县| 陆良县| 衡水市| 海安县| 遂溪县| 剑阁县| 桑日县| 同德县| 昌平区| 突泉县| 海门市| 乐陵市| 永登县| 静海县|