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

Rust突破編譯器限制構(gòu)造可修改的全局變量

 更新時(shí)間:2023年10月15日 11:10:00   作者:garfileo  
這篇文章主要為大家介紹了Rust突破編譯器限制構(gòu)造可修改的全局變量示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

問(wèn)題

在前面一些章節(jié)里,在使用正則表達(dá)式對(duì)文本進(jìn)行分割時(shí),皆使用局部變量存儲(chǔ)正則表達(dá)式,且皆為硬代碼——在程序運(yùn)行時(shí)無(wú)法修改正則表達(dá)式。本章嘗試構(gòu)造一個(gè)可在運(yùn)行時(shí)被修改的全局變量用以表達(dá)正則表達(dá)式。

失敗的全局原始指針

倘若將原始指針作為全局變量,在程序運(yùn)行時(shí),可以令其指向與其類(lèi)型相匹配的任何一個(gè)值,這是我想要的全局變量。于是,試著寫(xiě)出以下代碼:

use regex::Regex;
use std::ptr::null_mut;
let a: *mut Regex = null_mut();

fn main() {
    a = Box::into_raw(Box::new(Regex::new(" *@ *")));
    let v = (*a).unwrap().split("num@ 123@456  @ 789");
    for i in v {
        println!("{}", i);
    }
}

Rust 編譯器編譯上述代碼時(shí)會(huì)報(bào)錯(cuò),建議使用 const 或 static 代替全局變量 a 的定義語(yǔ)句中的 let,亦即 Rust 語(yǔ)言不允許使用 let 定義全局變量。const 修飾的全局變量,其值不可修改。static 修飾的全局變量,其值可修改。故而,我將變量 a 的定義修改為

static a: *mut Regex = null_mut();

Rust 編譯器依然報(bào)錯(cuò),稱(chēng) *mut regex::Regex 類(lèi)型的值不能被不同的線程安全共享,雖不甚知其意,但也應(yīng)知此路不通了。

也許在素有經(jīng)驗(yàn)的 Rust 程序員看來(lái),上述代碼會(huì)令他一言難盡,但是如果我說(shuō)通過(guò)以上代碼可以看出 Rust 語(yǔ)言并不希望程序員使用全局變量,料想不會(huì)引起他的反對(duì)。Rust 不希望什么,那是它的事,而我卻需要它。現(xiàn)在的問(wèn)題是,無(wú)法構(gòu)造全局原始指針。Rust 編譯器給出的建議是,如果想讓 *mut regex::Regex 類(lèi)型的指針作為全局變量,前提是需要為該類(lèi)型實(shí)現(xiàn) Sync 特性。這個(gè)建議對(duì)于目前的我來(lái)說(shuō)是超綱的,所以我完全可以認(rèn)為,在 Rust 語(yǔ)言中不允許出現(xiàn)全局原始指針。

Option<T> 于事無(wú)補(bǔ)

在表示空值方面,Option<T> 類(lèi)型可以代替原始指針,用該類(lèi)型封裝原始指針是否能作為全局變量呢?試試看:

static foo: Option<*mut i32> = None;
fn main() {
    let a = 3;
    foo = Some(&a as *mut i32);
    println!("{:?}", foo);
}

答案是否定的。Rust 編譯器依然稱(chēng):

`*mut i32` cannot be shared between threads safely

并建議

shared static variables must have a type that implements `Sync`

此路依然不通。

結(jié)構(gòu)體屏障

無(wú)論是直接用原始指針,還是用 Option<T> 封裝原始指針,在構(gòu)造全局變量時(shí),都會(huì)導(dǎo)致原始指針直接暴露在 Rust 編譯器面前,而編譯器堅(jiān)持認(rèn)為,所有的全局變量類(lèi)型都應(yīng)該實(shí)現(xiàn) Sync 特性?,F(xiàn)在,換一個(gè)思路,倘若將原始指針類(lèi)型封裝在結(jié)構(gòu)體中,是否可以騙過(guò)編譯器呢?

以下代碼將 *mut i32 類(lèi)型的指針?lè)庋b在一個(gè)結(jié)構(gòu)體類(lèi)型中,并使用該結(jié)構(gòu)體類(lèi)型構(gòu)造全局變量:

#[derive(Debug)]
struct Foo {
    data: *mut i32
}
static mut A: Foo = Foo{data: std::ptr::null_mut()};
fn main() {
    unsafe {
        println!("{:?}", A);
    }
}

上述程序可以通過(guò)編譯,其輸出為

Foo { data: 0x0 }

以下代碼嘗試能否修改 A.data 的值:

let mut a = 3;
unsafe {
    A.data = &mut a as *mut i32;
    println!("{:?}", A);
    println!("{}", *A.data);
}

依然能通過(guò)編譯,其輸出結(jié)果與以下結(jié)果類(lèi)似:

Foo { data: 0x7fff64cdecb4 }
3

這樣騙編譯器,好么?我不知道。Rust 標(biāo)準(zhǔn)庫(kù)在 std::marker::Sync 的文檔中提到,所有的基本類(lèi)型,復(fù)合類(lèi)型(元組、結(jié)構(gòu)體和枚舉),引用,Vec<T>,Box<T 以及大多數(shù)集合類(lèi)型等皆實(shí)現(xiàn)了 Sync 特性,所以上述手法并不能稱(chēng)為「騙」。

回到本章開(kāi)始的問(wèn)題,現(xiàn)在可寫(xiě)出以下代碼:

use regex::Regex;
use std::ptr::null_mut;
#[derive(Debug)]
struct Foo {
    data: *mut Regex
}
static mut A: Foo = Foo{data: null_mut()};
fn main() {
    unsafe {
        A = Foo {data: Box::into_raw(Box::new(Regex::new(" *@ *").unwrap()))};
        let v = (*A.data).split("num@ 123@456  @ 789");
        for i in v {
            println!("{}", i);
        }
        let _ = Box::from_raw(A.data);
    }
}

注意,上述代碼中的 let _ = ... 表示不關(guān)心右側(cè)函數(shù)調(diào)用的返回值,但是該行代碼可將 A.data 指向的內(nèi)存空間歸還于 Rust 的智能指針管理系統(tǒng),從而實(shí)現(xiàn)自動(dòng)釋放。

制造內(nèi)存泄漏

上述基于原始指針的全局變量構(gòu)造方法似乎并不為 Rust 開(kāi)發(fā)者欣賞,因?yàn)樵谒麄冄劾铮魏我粋€(gè)原始指針都像一個(gè)不知道什么時(shí)候會(huì)被一腳踩上去的地雷,他們更喜歡是引用。

下面嘗試使用引用構(gòu)造全局變量。由于引用不具備空值,所以必須使用 Option<T> 進(jìn)行封裝,例如

use regex::Regex;
static mut A: Option<&Regex> = None;

fn main() {
    unsafe {
        let re = Regex::new(" *@ *").unwrap();
        A = Some(&re);
        // ... 待補(bǔ)充
    }
}

Rust 編譯器對(duì)上述代碼給出的錯(cuò)誤信息是,re 被一個(gè)全局變量借用,但是前者的壽命短于后者,亦即當(dāng)后者還存在時(shí),前者已經(jīng)死亡,導(dǎo)致后者引用失效。在 C 語(yǔ)言中,這種錯(cuò)誤就是鼎鼎有名的「懸垂指針」錯(cuò)誤,Rust 編譯器會(huì)盡自己最大能力去阻止此類(lèi)錯(cuò)誤。

不過(guò),Rust 標(biāo)準(zhǔn)庫(kù)給我們留了一個(gè)后門(mén),使用 Box<T> 的 leak 方法可將位于堆空間的值的壽命提升為全局變量級(jí)別的壽命:

unsafe {
    let re = Box::new(Regex::new(" *@ *").unwrap());
    A = Some(Box::leak(re));
    let v = A.unwrap().split("num@ 123@456  @ 789");
    for i in v {
        println!("{}", i);
    }
}

需要注意的是,Box::leak 名副其實(shí),會(huì)導(dǎo)致內(nèi)存泄漏,因?yàn)槎芽臻g的值其壽命經(jīng) Box::leak 提升后,與程序本身相同,無(wú)法回收。Rust 官方說(shuō),如果你介意這樣的內(nèi)存泄漏,那就需要考慮走原始指針路線。

延遲初始化

對(duì)于支持運(yùn)行時(shí)修改的全局變量,還有一類(lèi)方法是將全局變量的初始化推遲在程序運(yùn)行時(shí),但該類(lèi)方法要么依賴(lài)第三方庫(kù)(crate),例如 lazy_static,要么是標(biāo)準(zhǔn)庫(kù)目前尚未穩(wěn)定的功能 OnceCell,此外該類(lèi)方法只能對(duì)全局變量完成一次賦值。這些方法,rzeo 并不打算使用,故而略過(guò)。

值的所有權(quán)轉(zhuǎn)移

基于值的所有權(quán)轉(zhuǎn)移也能實(shí)現(xiàn)在程序的運(yùn)行時(shí)修改全局變量的值。例如

use regex::Regex;
static mut A: Option<Regex> = None;

fn main() {
    unsafe {
        let re = Regex::new(" *@ *").unwrap();
        A = Some(re);
        let v = A.unwrap().split("num@ 123@456  @ 789");
        for i in v {
            println!("{}", i);
        }
    }
}

不過(guò),上述代碼無(wú)法通過(guò)編譯,原因是 Option<T> 的實(shí)例方法 unwrap 需要轉(zhuǎn)移實(shí)例的所有權(quán)——消耗一個(gè)臨時(shí)變量,但是上述代碼中的 Option<T> 的實(shí)例 A 是全局變量,與程序同壽,其所有權(quán)無(wú)法轉(zhuǎn)移。有兩種方法可規(guī)避該錯(cuò)誤,一種是

unsafe {
    let re = Regex::new(" *@ *").unwrap();
    A = Some(re);
    match A {
        Some(ref b) => {
            let v = b.split("num@ 123@456  @ 789");
            for i in v {
                println!("{}", i);
            }
        },
        None => panic!("...")
    }
}

另一種是使用 Option<T> 的 as_ref 方法,將類(lèi)型 &Option<T> 轉(zhuǎn)換為類(lèi)型 Option<&T>,然后使用 Option<&T> 的 unwrap 方法:

unsafe {
    let re = Regex::new(" *@ *").unwrap();
    A = Some(re);
    let v = A.as_ref().unwrap().split("num@ 123@456  @ 789");
    for i in v {
        println!("{}", i);
    }
}

不妨將 as_ref 方法視為上述模式匹配代碼的簡(jiǎn)化。

小結(jié)

全局變量是構(gòu)成程序的不安全因素之一,但它并非洪水猛獸,只要保證程序在任一時(shí)刻全局變量不會(huì)被多個(gè)線程同時(shí)修改即可。如果全局變量給程序帶來(lái)了災(zāi)難,這往往意味著是程序的設(shè)計(jì)出現(xiàn)了嚴(yán)重問(wèn)題。我認(rèn)為 Rust 對(duì)全局變量的限制太過(guò)于嚴(yán)厲,特別是在禁止直接將原始指針作為全局變量這一方面,畢竟即使不使用原始指針,對(duì)全局變量的修改在 Rust 語(yǔ)言看來(lái),也是不安全的。既然都不安全,何必五十步笑百步。

以上就是Rust突破編譯器限制構(gòu)造可修改的全局變量的詳細(xì)內(nèi)容,更多關(guān)于Rust全局變量的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Rust中的所有權(quán)機(jī)制

    詳解Rust中的所有權(quán)機(jī)制

    Rust?語(yǔ)言提供了跟其他系統(tǒng)編程語(yǔ)言相同的方式來(lái)控制你使用的內(nèi)存,但擁有數(shù)據(jù)所有者在離開(kāi)作用域后自動(dòng)清除其數(shù)據(jù)的功能意味著你無(wú)須額外編寫(xiě)和調(diào)試相關(guān)的控制代碼,這篇文章主要介紹了Rust中的所有權(quán)機(jī)制,需要的朋友可以參考下
    2022-10-10
  • 使用Rust語(yǔ)言搞定圖片上傳功能的示例詳解

    使用Rust語(yǔ)言搞定圖片上傳功能的示例詳解

    這篇文章主要為大家詳細(xì)介紹了如何使用Rust語(yǔ)言搞定圖片上傳功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以參考一下
    2025-08-08
  • 為什么要使用 Rust 語(yǔ)言、Rust 語(yǔ)言有什么優(yōu)勢(shì)

    為什么要使用 Rust 語(yǔ)言、Rust 語(yǔ)言有什么優(yōu)勢(shì)

    雖然 Rust 是一種通用的多范式語(yǔ)言,但它的目標(biāo)是 C 和 C++占主導(dǎo)地位的系統(tǒng)編程領(lǐng)域,很多朋友會(huì)問(wèn)rust語(yǔ)言難學(xué)嗎?rust語(yǔ)言可以做什么,今天帶著這些疑問(wèn)通過(guò)本文詳細(xì)介紹下,感興趣的朋友一起看看吧
    2022-10-10
  • Rust在寫(xiě)庫(kù)時(shí)實(shí)現(xiàn)緩存的操作方法

    Rust在寫(xiě)庫(kù)時(shí)實(shí)現(xiàn)緩存的操作方法

    Moka是一個(gè)用于Rust的高性能緩存庫(kù),它提供了多種類(lèi)型的緩存數(shù)據(jù)結(jié)構(gòu),包括哈希表、LRU(最近最少使用)緩存和?支持TTL(生存時(shí)間)緩存,這篇文章給大家介紹Rust在寫(xiě)庫(kù)時(shí)實(shí)現(xiàn)緩存的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2024-01-01
  • Rust中non_exhaustive的enum使用確保程序健壯性

    Rust中non_exhaustive的enum使用確保程序健壯性

    這篇文章主要為大家介紹了Rust中non_exhaustive的enum使用確保程序健壯性示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • Rust動(dòng)態(tài)數(shù)組Vec基本概念及用法

    Rust動(dòng)態(tài)數(shù)組Vec基本概念及用法

    Rust中的Vec是一種動(dòng)態(tài)數(shù)組,它可以在運(yùn)行時(shí)自動(dòng)調(diào)整大小,本文主要介紹了Rust動(dòng)態(tài)數(shù)組Vec基本概念及用法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • 最新評(píng)論

    七台河市| 周至县| 永丰县| 岳阳市| 凤庆县| 曲麻莱县| 昔阳县| 蒙城县| 西吉县| 晋江市| 迁安市| 忻州市| 丰镇市| 嘉定区| 平顶山市| 军事| 望都县| 汉川市| 武汉市| 乌鲁木齐县| 余干县| 靖江市| 曲沃县| 通河县| 湖州市| 金山区| 鹰潭市| 上杭县| 台北县| 新昌县| 牙克石市| 沿河| 柏乡县| 高阳县| 广元市| 濉溪县| 开封县| 南昌市| 石渠县| 贡觉县| 禹州市|