亚欧色一区w666天堂,色情一区二区三区免费看,少妇特黄A片一区二区三区,亚洲人成网站999久久久综合,国产av熟女一区二区三区

  • 發布文章
  • 消息中心
點贊
收藏
評論
分享
原創

rust 頂層設計

2023-11-21 01:31:19
68
0


rust起源


任何一門語言的興起,都是為了解決以往其他語言所面臨的問題或挑戰 -- 魯迅

自操作系統誕生以來,系統級主流編程語言,從匯編語言發展到C和C++, 已經發展了近50年。但仍然存在兩個難題:

  • 很難編寫內存安全的代碼
  • 很難編寫線程安全的代碼

這兩個問題本質的原因是:C/C++屬于類型不安全的語言。

因此,需要一個可以提供開發效率高、代碼容易維護、性能還能與C/C++媲美,同時還得保證安全性的語言。

rust應運而生!

由此可見,rust之所以安全,是因為從設計上就是解決傳統編程不安全的問題的,這是他的內在靈活。

 

rust核心解決的問題


內存安全


在編程中,內存管理是一項具有挑戰性的任務。錯誤的內存管理可能導致諸如數據競爭,空指針引用,內存泄漏等問題,這些問題常常導致程序崩潰或者安全漏洞。傳統的編程語言如C和C++雖然給予了開發者對內存的高度控制權,但同時也使得開發者需要對內存管理負責,而內存管理的錯誤往往導致嚴重的后果。

類似新興的編程語言golang,引入了垃圾回收機制,通過語言自動處理內存分配和回收,實現了內存安全。但是,垃圾回收會有較大的性能損失,并且存在 “stop the world” 的問題!

Rust在這方面采取了一種全新的方法,它引入了所有權(ownership)、借用(borrowing)和生命周期(lifetime)的概念,用以保證內存安全而無需垃圾回收

Rust的這種設計讓編譯器在編譯階段就能捕捉到許多常見的內存錯誤,從而極大地提高了程序的安全性和穩定性。

并發安全

并發編程是另一項具有挑戰性的任務,尤其是在多線程環境中。并發程序中的數據競爭問題是導致程序錯誤的主要原因之一。傳統的編程語言往往需要開發者自行使用鎖等同步機制來避免數據競爭,而這對于開發者來說是一項非常繁瑣且容易出錯的任務。

Rust通過其所有權系統,配合可變借用限制條件(同一作用域內,可以多個入口度讀變量;同一作用域內,只允許一個入口修改變量;同一作用域內,不允許同時存在讀入口和寫入口),提供了一種在編譯時檢測數據競爭的機制。

這種設計使得在Rust中寫并發程序變得更加安全且容易。通過在編譯階段就消除數據競爭,Rust讓并發編程變得更加簡單和安全。

零成本抽象

在許多高級編程語言中,語言提供的抽象往往會導致運行時的性能損失。例如,虛函數、動態類型、垃圾收集等特性在提供便利的同時,也可能導致程序的性能下降。為了提高性能,開發者往往需要做出妥協,放棄一些便利的抽象。

Rust提供了“零成本抽象”的承諾。

在Rust中,抽象不會導致運行時的性能損失。這是因為Rust的設計哲學是:讓那些在編譯階段就能解決的問題,在編譯階段就解決掉。這種設計使得開發者可以在不犧牲性能的前提下,使用高級的抽象來編寫代碼。

trait 和 宏是rust 零成本抽象的基石!

 

跨平臺開發

在現代軟件開發中,跨平臺性成為了一項重要的需求。開發者們希望他們編寫的代碼能夠在各種不同的平臺上運行,而不需要進行大量的修改。然而,不同的平臺往往有不同的系統調用和硬件接口,這使得跨平臺開發變得非常復雜。

Rust為跨平臺開發提供了強大的支持。它的標準庫提供了一系列的抽象,可以在不同的平臺上進行一致的系統調用。

此外,Rust還支持WebAssembly,使得Rust代碼可以在瀏覽器中運行。

性能優化

性能是任何編程語言都需要考慮的問題。

Rust通過零成本抽象、精確的內存管理和高效的并發處理,提供了卓越的性能。

Rust的代碼執行效率可以與C++相媲美,而且由于其內存和并發安全的設計,開發者可以更加集中精力在業務邏輯上,而不需要過多地擔心性能優化。

 

內存安全補充

常見的內存訪問問題有如下幾個:

  • 引用空指針
  • 使用未初始化內存
  • 訪問已釋放的內存
  • 內存訪問越界
  • 重復釋放
  • 內存泄漏

為了保證內存安全,Rust語言建立了嚴格的安全內存管理模型。


無空值設計+強制初始化

(1)rust 并不支持‘空值’,不存在null、nil、NULL等,取而代之的是Option。 而Option 是一個枚舉類型,如果要使用其中的值,就必須進行模式匹配,這樣就強制編程者去關注是否為空。進而避免了空值引用(引用空指針)。

(2)rust 在定義變量時,不允許定義未初始化的變量。舉例而言,在c語言中,申請一個結構體空間,那么得到一個結構體指針,但結構體指針所指向的內容依然還是空值,此時你便可以讀寫在實例;在rust中,申請一個結構體實例,就必須要求先對結構體所有字段進行初始化,才可以使用。

以上兩點,就可以保證rust中內存引用不會出現空指針引用,或訪問到未初始化內存!

 

所有權系統 

(1)rust 中,每個內存分配的實例,都會綁定到一個變量x上,則x擁有該實例的所有權。一個實例,任意時刻只能被一個變量擁有所有權。

(2)當x離開其作用域時,x將被自動銷毀;同時,x所指向的內存也會自動被釋放。

(3)在x作用域內,x可以將其實例的所有權移交給另一個變量y。

rust的所有權系統,可以確保一個實例由唯一的變量持有所有權,并將其生命周期與持有所有權的變量綁定;從而避免重復釋放,避免內存泄漏。

 

借用和借用檢查(生命周期)

rust的所有權系統,雖然為內存分配和釋放提供了有力保障,但對編程過程中數據共享卻形成和非常大的阻礙。為了解決這個問題,rust設計了借用機制。借用,和其他語言中的‘引用’概念相似,是一個指向實例的指針。

rust中,通過借用,可以對實例進行讀取和修改,但不會持有該數據所有權;那么當該借用變量離開其作用域時,則只回收該變量本身,而不會釋放其指向的內存分配。

rust 通過借用解決了數據共享問題,同時避免了重復釋放。

但借用會引發另一個問題:如果在借用過程中,持有實例所有權的變量先走出了作用域,那么實例內存空間就會被釋放,此時引用變量再去訪問這塊內存,就會形成 ‘訪問已釋放的內存’ 的錯誤。為了解決這個問題,rust設計了借用檢查機制 。借用檢測機制的核心是:通過一種機制,確保借用變量的生命周期內,實例的生命周期不能提前結束。

而借用檢查的實現方式:生命周期檢測。 編程中,每一個變量都有一個生存期(也稱生命周期);rust 的引用的生命周期,關聯著 實例擁有者變量的生存期(記為‘o),和 引用變量的生存期(記為 ‘p);那么,要求 'p 是包含于 'o的(即實例變量的生存周期一定要比引用變量的生存期要長)。

{
    let r;

    {
        let x = 5;
        r = &x;
    }

    println!("r: {}", r);
}


上述的一段代碼,rust編譯就會報錯,提示x生存不夠長。我們將r和x的生存期標注出來:

    let r;                 

    {
        let x = 5;         // 'x start
        r = &x;            // 'r start 
    }                      // 'x end

    println!("r: {}", r);  // 'r end
}


如上代碼標注,借用檢測器,在確定引用時,就會比較引用變量本身和實例擁有者變量的生命周期長短,并確保引用的生存期不能長于實例的生存期。

 

另一方面,當引用在函數中傳遞時(或從函數中返回引用),此時就丟失了引用所綁定的實例變量的生存期信息,此時編譯器在確認引用生存期時將面臨挑戰。有如下示例函數:

fn longer(p1:&String, p2:&String) -> &String {

    if pl.len() >= p2.len() {
        p1
    }
    p2
}

longer函數返回兩個字符串中更長的一個。

... 
// p1 start
// p2 start 

let rp = longer(p1, p2);  // rp start 

// p1 end 

println!( "the longer pointer is {}", rp); // rp end

// p2 end 

有如上示例代碼,通過longer函數決策,rp是p1、p2 其中的一個,但具體是哪一個,卻不知道。當rust編譯器,嘗試判斷p1 和p2 的實例變量的生命周期與rp的生命周期的長短時,無法選擇比較目標,而選擇報錯。

此時,編譯器需要程序員的協助,幫它指定一個比較標準。而實現該目標的方法,就是“生命周期標注”!

生命周期標注,是用來告訴編譯器,一個可以評估引用與其關聯的實例生存期長短的依據。值得注意的是,生命周期標注,只是引導編譯器進行評估比較,并沒有實際改變被標準的變量的生命周期!

fn longer<'a>(p1: &'a String, p2: &'a String) -> &'a String {
      if p1.len() >= p2.len() {
          return p1;
      }
      p2
  }

對longer函數進行如上修改。該函數簽名表示:longer函數接收入參p1,p2,返回值生存期關系為:p1 p2的實例變量的生存期,不能短于該函數返回值的生存期。

let p1 = String::from("123456");
let p2 = String::from("789");

let rp = longer(&p1, &p2);

drop(p2);

println!("{}", rp);

 

上述代碼編譯會報錯,其中drop(p2)處,在rp引用未結束前嘗試銷毀p2(縮短p2的生存期),導致p2的生存期無法滿足rp的引用需求,該問題會被編譯器執行rp生命周期檢測是發現并暴露出來。

 

0條評論
0 / 1000
huskar
20文章數
3粉絲數
huskar
20 文章 | 3 粉絲
原創

rust 頂層設計

2023-11-21 01:31:19
68
0


rust起源


任何一門語言的興起,都是為了解決以往其他語言所面臨的問題或挑戰 -- 魯迅

自操作系統誕生以來,系統級主流編程語言,從匯編語言發展到C和C++, 已經發展了近50年。但仍然存在兩個難題:

  • 很難編寫內存安全的代碼
  • 很難編寫線程安全的代碼

這兩個問題本質的原因是:C/C++屬于類型不安全的語言。

因此,需要一個可以提供開發效率高、代碼容易維護、性能還能與C/C++媲美,同時還得保證安全性的語言。

rust應運而生!

由此可見,rust之所以安全,是因為從設計上就是解決傳統編程不安全的問題的,這是他的內在靈活。

 

rust核心解決的問題


內存安全


在編程中,內存管理是一項具有挑戰性的任務。錯誤的內存管理可能導致諸如數據競爭,空指針引用,內存泄漏等問題,這些問題常常導致程序崩潰或者安全漏洞。傳統的編程語言如C和C++雖然給予了開發者對內存的高度控制權,但同時也使得開發者需要對內存管理負責,而內存管理的錯誤往往導致嚴重的后果。

類似新興的編程語言golang,引入了垃圾回收機制,通過語言自動處理內存分配和回收,實現了內存安全。但是,垃圾回收會有較大的性能損失,并且存在 “stop the world” 的問題!

Rust在這方面采取了一種全新的方法,它引入了所有權(ownership)、借用(borrowing)和生命周期(lifetime)的概念,用以保證內存安全而無需垃圾回收

Rust的這種設計讓編譯器在編譯階段就能捕捉到許多常見的內存錯誤,從而極大地提高了程序的安全性和穩定性。

并發安全

并發編程是另一項具有挑戰性的任務,尤其是在多線程環境中。并發程序中的數據競爭問題是導致程序錯誤的主要原因之一。傳統的編程語言往往需要開發者自行使用鎖等同步機制來避免數據競爭,而這對于開發者來說是一項非常繁瑣且容易出錯的任務。

Rust通過其所有權系統,配合可變借用限制條件(同一作用域內,可以多個入口度讀變量;同一作用域內,只允許一個入口修改變量;同一作用域內,不允許同時存在讀入口和寫入口),提供了一種在編譯時檢測數據競爭的機制。

這種設計使得在Rust中寫并發程序變得更加安全且容易。通過在編譯階段就消除數據競爭,Rust讓并發編程變得更加簡單和安全。

零成本抽象

在許多高級編程語言中,語言提供的抽象往往會導致運行時的性能損失。例如,虛函數、動態類型、垃圾收集等特性在提供便利的同時,也可能導致程序的性能下降。為了提高性能,開發者往往需要做出妥協,放棄一些便利的抽象。

Rust提供了“零成本抽象”的承諾。

在Rust中,抽象不會導致運行時的性能損失。這是因為Rust的設計哲學是:讓那些在編譯階段就能解決的問題,在編譯階段就解決掉。這種設計使得開發者可以在不犧牲性能的前提下,使用高級的抽象來編寫代碼。

trait 和 宏是rust 零成本抽象的基石!

 

跨平臺開發

在現代軟件開發中,跨平臺性成為了一項重要的需求。開發者們希望他們編寫的代碼能夠在各種不同的平臺上運行,而不需要進行大量的修改。然而,不同的平臺往往有不同的系統調用和硬件接口,這使得跨平臺開發變得非常復雜。

Rust為跨平臺開發提供了強大的支持。它的標準庫提供了一系列的抽象,可以在不同的平臺上進行一致的系統調用。

此外,Rust還支持WebAssembly,使得Rust代碼可以在瀏覽器中運行。

性能優化

性能是任何編程語言都需要考慮的問題。

Rust通過零成本抽象、精確的內存管理和高效的并發處理,提供了卓越的性能。

Rust的代碼執行效率可以與C++相媲美,而且由于其內存和并發安全的設計,開發者可以更加集中精力在業務邏輯上,而不需要過多地擔心性能優化。

 

內存安全補充

常見的內存訪問問題有如下幾個:

  • 引用空指針
  • 使用未初始化內存
  • 訪問已釋放的內存
  • 內存訪問越界
  • 重復釋放
  • 內存泄漏

為了保證內存安全,Rust語言建立了嚴格的安全內存管理模型。


無空值設計+強制初始化

(1)rust 并不支持‘空值’,不存在null、nil、NULL等,取而代之的是Option。 而Option 是一個枚舉類型,如果要使用其中的值,就必須進行模式匹配,這樣就強制編程者去關注是否為空。進而避免了空值引用(引用空指針)。

(2)rust 在定義變量時,不允許定義未初始化的變量。舉例而言,在c語言中,申請一個結構體空間,那么得到一個結構體指針,但結構體指針所指向的內容依然還是空值,此時你便可以讀寫在實例;在rust中,申請一個結構體實例,就必須要求先對結構體所有字段進行初始化,才可以使用。

以上兩點,就可以保證rust中內存引用不會出現空指針引用,或訪問到未初始化內存!

 

所有權系統 

(1)rust 中,每個內存分配的實例,都會綁定到一個變量x上,則x擁有該實例的所有權。一個實例,任意時刻只能被一個變量擁有所有權。

(2)當x離開其作用域時,x將被自動銷毀;同時,x所指向的內存也會自動被釋放。

(3)在x作用域內,x可以將其實例的所有權移交給另一個變量y。

rust的所有權系統,可以確保一個實例由唯一的變量持有所有權,并將其生命周期與持有所有權的變量綁定;從而避免重復釋放,避免內存泄漏。

 

借用和借用檢查(生命周期)

rust的所有權系統,雖然為內存分配和釋放提供了有力保障,但對編程過程中數據共享卻形成和非常大的阻礙。為了解決這個問題,rust設計了借用機制。借用,和其他語言中的‘引用’概念相似,是一個指向實例的指針。

rust中,通過借用,可以對實例進行讀取和修改,但不會持有該數據所有權;那么當該借用變量離開其作用域時,則只回收該變量本身,而不會釋放其指向的內存分配。

rust 通過借用解決了數據共享問題,同時避免了重復釋放。

但借用會引發另一個問題:如果在借用過程中,持有實例所有權的變量先走出了作用域,那么實例內存空間就會被釋放,此時引用變量再去訪問這塊內存,就會形成 ‘訪問已釋放的內存’ 的錯誤。為了解決這個問題,rust設計了借用檢查機制 。借用檢測機制的核心是:通過一種機制,確保借用變量的生命周期內,實例的生命周期不能提前結束。

而借用檢查的實現方式:生命周期檢測。 編程中,每一個變量都有一個生存期(也稱生命周期);rust 的引用的生命周期,關聯著 實例擁有者變量的生存期(記為‘o),和 引用變量的生存期(記為 ‘p);那么,要求 'p 是包含于 'o的(即實例變量的生存周期一定要比引用變量的生存期要長)。

{
    let r;

    {
        let x = 5;
        r = &x;
    }

    println!("r: {}", r);
}


上述的一段代碼,rust編譯就會報錯,提示x生存不夠長。我們將r和x的生存期標注出來:

    let r;                 

    {
        let x = 5;         // 'x start
        r = &x;            // 'r start 
    }                      // 'x end

    println!("r: {}", r);  // 'r end
}


如上代碼標注,借用檢測器,在確定引用時,就會比較引用變量本身和實例擁有者變量的生命周期長短,并確保引用的生存期不能長于實例的生存期。

 

另一方面,當引用在函數中傳遞時(或從函數中返回引用),此時就丟失了引用所綁定的實例變量的生存期信息,此時編譯器在確認引用生存期時將面臨挑戰。有如下示例函數:

fn longer(p1:&String, p2:&String) -> &String {

    if pl.len() >= p2.len() {
        p1
    }
    p2
}

longer函數返回兩個字符串中更長的一個。

... 
// p1 start
// p2 start 

let rp = longer(p1, p2);  // rp start 

// p1 end 

println!( "the longer pointer is {}", rp); // rp end

// p2 end 

有如上示例代碼,通過longer函數決策,rp是p1、p2 其中的一個,但具體是哪一個,卻不知道。當rust編譯器,嘗試判斷p1 和p2 的實例變量的生命周期與rp的生命周期的長短時,無法選擇比較目標,而選擇報錯。

此時,編譯器需要程序員的協助,幫它指定一個比較標準。而實現該目標的方法,就是“生命周期標注”!

生命周期標注,是用來告訴編譯器,一個可以評估引用與其關聯的實例生存期長短的依據。值得注意的是,生命周期標注,只是引導編譯器進行評估比較,并沒有實際改變被標準的變量的生命周期!

fn longer<'a>(p1: &'a String, p2: &'a String) -> &'a String {
      if p1.len() >= p2.len() {
          return p1;
      }
      p2
  }

對longer函數進行如上修改。該函數簽名表示:longer函數接收入參p1,p2,返回值生存期關系為:p1 p2的實例變量的生存期,不能短于該函數返回值的生存期。

let p1 = String::from("123456");
let p2 = String::from("789");

let rp = longer(&p1, &p2);

drop(p2);

println!("{}", rp);

 

上述代碼編譯會報錯,其中drop(p2)處,在rp引用未結束前嘗試銷毀p2(縮短p2的生存期),導致p2的生存期無法滿足rp的引用需求,該問題會被編譯器執行rp生命周期檢測是發現并暴露出來。

 

文章來自個人專欄
文章 | 訂閱
0條評論
0 / 1000
請輸入你的評論
1
1