當前位置:首頁 » 編程軟體 » rust編譯用時分析

rust編譯用時分析

發布時間: 2022-10-16 00:27:49

java和Rust在實現多線程編程時的異同

Java的實現
打開Follower.java里的這個函數

這里的Follower.this.invitations就是我們的消息隊列,定義是:private LinkedList<Invitation> invitations;LinkedList不是線性安全的集合,需要我們加同步。具體的同步方法就是函數里寫的,通過Java常見的用wait,notify和notifyall給對象加鎖。
處理並發有wait、notify和notiyall,有興趣的朋友可以去這里了解一下:http://www.importnew.com/16453.html。Follower就是一個等待leader發送invitation,處理並返回結果的過程。
Leader.java
這么一段代碼:

裡面就是Leader發送邀請inv,並等待follower返回結果的大概邏輯,通過對消息體加鎖,是Java傳統的實現多線程並發的方式。還有消費者的消息隊列也會加鎖,在Java里,有個對象叫LinkedBlockingQueue,是不用加鎖就可以put和take的,但在例子里,我們選用了更簡單的LinkedList,也是為了表現一下加鎖的邏輯。
Rust的實現
Leader的結構為:

Follower的結構為:

對於其他語言轉過來的同學,這里的Vec,i32,bool都很好理解,不過裡面出現的Arc和Mutex,Sender,Receiver就是新東西了,上面這4個都是Rust標准庫的東西,也是這次分享要介紹的重點對象,是這4個東西共同實現了消息的生產,傳遞和消費。
下面簡單介紹一下分別是做什麼用的:
Arc<T>實現了sync介面。Sync介面是做什麼呢?權威資料是這么說的:當一個類型T實現了Sync,它向編譯器表明這個類型在多線程並發時沒有導致內存不安全的可能性。
如果看不懂不要緊,我們先看看實際中是怎麼用的:

在這個例子里,我們關注這幾句:
let data = Arc::new(Mutex::new(vec![1u32, 2, 3]));
let data = data.clone();
let mut data = data.lock().unwrap();
下面分別解釋一下是做什麼的:
簡單的說Arc::new表明了這是通過clone()方法來使用的,每clone,都會給該對象原子計數+1,通過引用計數的方法來保證對象只要還被其中任何一個線程引用就不會被釋放掉,從而保證了前面說的:這個類型在多線程並發時沒有導致內存不安全的可能性。
如果我們不定義為Arc<>就傳到其他線程使用,編譯器會報:
error: capture of moved value: `data`
data[i] += 1;
我們可以記住clone()就是Arc的用法。
接下來我們看Mutex:
Mutex實現了send介面。同樣,在權威資料里是這么描述的:這個類型的所有權可以在線程間安全的轉移
那我們又是怎麼用Mutex的呢?就是用lock().unwrap()。lock()的作用是獲取對象,如果當前有其他線程正在使用Mutex<T>裡面的T對象時,本線程就會阻塞,從而保證同時只有一個線程來訪問對象,mutex也另外提供了try_lock()的方法,是不阻塞的,只要其他線程被佔用,就返回err,通常Arc和Mutex都是一起使用的。
回到我最原始的題目,Mutex和Arc實現了對象本身的線程共享,但是在線程間如何傳遞這個對象呢?就是靠channel,channel通常是這么定義的let (tx, rx) = mpsc::channel();它會返回兩個對象tx和rx,就是之前我提到的sender和receiver。
在我的Rust實現里,關鍵的語句是以下幾個:
let leaders = (0..leader_cnt).map(|i|
Arc::new(Mutex::new(Leader::new(i,dance_types.len() as i32)))
).collect::<Vec<_>>();
這一句是new一堆leader出來,Arc和Mutex表明leader是可以多線程共享和訪問的。
同樣Follower也是:
let followers = (0..follower_cnt).map(|i|
Arc::new(Mutex::new(Follower::new(i,dance_types.len() as i32,leader_cnt)))
).collect::<Vec<_>>();
接下來這幾句就有點不好理解了。

這里定義了一堆的sender和receiver,其中把他們都作為leader和follower的成員變數存起來。大概意思就是每一個leader都通過sender列表可以發送invitation給所有follower,同時又有單個receiver來接受所有follower發給自己的處理結果inviresult。
同樣follower也是這么做。這樣在之後每一個follower和leader作為一個線程跑起來之後,都能在相互之間建立了一條通信的通道。
這個是和Java實現多線程並發最大的不同之處!Java是通過給對象加鎖,Rust是通過channel轉移對象的所有權,在代碼里,leader發送inv給folloer是下面這一句
match self.senders[*follower_id as usize].lock().unwrap().send(inv){,其中的lock().unwrap()是獲得該leader對該follower的發送通道的所有權,send(inv)就是轉移具體的發送對象invitation所有權了。
這個轉移按照我的理解,應該是內存拷貝。就是在follower接收的時候,let inv = match self.receiver.recv() { ,原來leader裡面的inv在send之後已經是不可訪問了,如果你之後再次訪問了inv,會報use of moved value錯誤,而follower裡面的inv則是在follower的棧里新生成的對象,所以,在Java裡面我只定義了invitation對象,但是在Rust裡面,我要再定義一個InviResult,因為我即使在follower線程裡面填了result欄位,leader線程也不能繼續訪問inv了。所以需要依靠follower再次發送一個invresult給leader,所以整個Rust程序大概就是這么一個思路。
實踐總結
之前我測試比較Java和Rust實現的性能時,由於沒有把調試信息去掉,導致Java比Rust慢很多,特別是那些調試信息都是調用String.format,這是比幾個string相加慢上10倍的方法,兩者都去掉調試信息後,leader和follower都會2000的時候,在我低端外星人筆記本里,性能差別大概是2倍吧,沒我想像中大,Rust的程序整個寫下來比較費力,一方面是對ownership機制不熟,思維沒有轉變過來,另一方面Rust的確需要開發者分部分精力到語法細節上。
編者註:馮總也有一些其它的實踐體會,請參見CSDN對馮耀明的專訪,請戳這里。也可以查看他的個人博客里的總結。
下面摘錄采訪中關於Rust的內容過來:
首先Rust裡面的ownership和lifetime概念真的很酷,就因為這個概念實現無內存泄露,野指針和安全並發。
其次,Rust的語法不簡單,也是有不少坑的,據說Rust的潛在用戶應該是現在的C和C++程序員,他們可能會覺得比較習慣,說不定還 覺得更簡單。由於ownership機制,一些在其他語言能夠跑通的程序在Rust下就要調整實現了,它會改變你寫程序的思維方式。據說一些寫Rust超 過半年的程序員已經愛上它了!
我對Rust感受較深的是下面幾點:
初學者不熟悉ownership機制,會無數次編譯失敗。但一旦編譯成功,那麼程序只剩下邏輯錯誤了。同樣,由於ownership機制,將來在項目里修改Rust代碼將可能是痛苦的過程,因為原來編譯通過的代碼可能加入新功能就編譯不過了,這是我的猜測。
Rust編譯速度慢,不過據說最近每一個Rust新發布的版本編譯速度都比之前的版本提高了30%。
Rust沒有類,有的是結構體加方法,我喜歡這種簡單的概念。
Rust沒有類繼承,只有介面,雖然介面可以提供默認的實現。這樣一來,在大型項目里原來類繼承來重用代碼的效果是否就要用成員變數實例來完成呢?
Rust沒有null,取而代之的是None和Option<T>,也因此,結構體在初始化的時候必須初始化所有欄位。
Rust有我一直很想要的錯誤值返回機制,而不必通過拋異常或者需要每每定義包含結果和錯誤體實現。
Rust用send和sync兩個介面來處理多線程並發,其中Arc<T>和Mutex<T>分別實現了這兩個介面,簡單易用。
Rust目前沒有一個強大的IDE,支持斷點調試,變數監控等。
它跟現在動態語言是兩個截然不同的方向,它適合一些資深的程序員,我倒是覺得有必要有這么一本書,叫《從C++到Rust,你需要改善的20個編程 習慣》,能從實踐上告訴開發者Rust里我們應該遵從什麼樣的編程習慣。Rust未來是否像C那樣流行開來成為新一代的主流語言沒有人能夠知道,但它絕對 是值得你去了解和關注的語言。
進一步的思考:反轉鏈表 - Java和Rust的不同實現
Rust的list應該怎麼定義,譬如反轉列表又是怎麼做呢?
由於ownership的機制和不存在空指針的情況,很多在其他帶GC的語言能夠跑起來的程序在Rust下面就要換一種做法。最近試用Rust的基礎數據結構時,更加加強了我的看法。下面以最原始的鏈表list為例。
在Java中,考慮最基本的鏈表定義
class ListNode {
int val;
ListNode next;

ListNode(int x) {
val = x;
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
sb.append(val);
ListNode pNext = this.next;
while (pNext != null) {
sb.append(",");
sb.append(pNext.val);
pNext = pNext.next;
}
sb.append("]");
return String.format("%s", sb.toString());
}
}
如果我們要反轉鏈表,可以這么做:
public ListNode reverseList(ListNode head) {
if (head == null) {
return null;
}
ListNode pNext = head.next;
ListNode pPrevious = null;
while (head != null) {
pNext = head.next;
head.next = pPrevious;
pPrevious = head;
head = pNext;
}
return pPrevious;
}
那如果我們按照一般思維,在Rust里對應的實現就是這樣子的:
struct ListNode{
id :i32,
next :Option<Box<ListNode>>
}
反轉鏈表:
fn reverseList2(head :&mut Option<Box<ListNode>>) -> Option<Box<ListNode>> {
match *head{
None => None,
Some(head) => {
let mut head = Some(head);
let mut pNext = head.unwrap().next;
let mut pPrevious:Option<Box<ListNode>> = None;
while true {
match head {
None =>{break;}
_ =>{}
}
pNext = head.unwrap().next;
head.unwrap().next = pPrevious;
pPrevious = head;
head = pNext;
}
pPrevious
}
}
}
然後編譯,報了以下錯誤:
=》match *head{

ERROR:cannot move out of borrowed content
=》 pNext = head.unwrap().next;
ERROR:cuse of moved value: `head`

這些錯誤就是因為Rust的ownership機制,讓我們無法像Java或者C++里保存臨時變數,特別是在循環里。反復試過各種寫法,都行不通。
最後,換成這么來做
鏈表定義:
use List::*;

enum List {
Cons1(i32, Box<List>),
Nil,
}

// Methods can be attached to an enum
impl List {
#[inline]
fn new() -> List {
Nil
}

#[inline]
fn prepend(self, elem: i32) -> List {
Cons1(elem, Box::new(self))
}

fn len(&self) -> i32 {
match *self {
Cons1(_, ref tail) => 1 + tail.len(),
Nil => 0
}
}

fn stringify(&self) -> String {
match *self {
Cons1(head, ref tail) => {
format!("{}, {}", head, tail.stringify())
},
Nil => {
format!("Nil")
},
}
}
}

fn reverseList(list:List, acc:List ) -> List{
match list{
Cons1(val,tail) => {
reverseList(*tail,acc.prepend(val))
}
Nil => acc
}
}

fn main() {
let mut head = List::new();
let mut i=0;
while i < 10 {
i+=1;
head = head.prepend(i);
}
println!("{:30}",head.stringify());
let result = List::new();
let result = reverseList(head,result);
<span style="white-space:pre"> </span>println!("{:30}",result.stringify());
}
從結果可以看到,鏈表已經實現反轉了。所以在Rust下面,很多做法都要換一下。有人說這就是Rust函數式編程的思維。我但願這種遞歸式的做法不會有溢出。

⑵ Rust VS Python:為什麼越來越流行,取代榜一 Python

2021 年,Python 又獲得了 TIOBE 年度編程語言,排名已經是第一。而 Rust 依然在 20 名以外。但依然有人認為,Rust 甚至可能取代 Python。不過這不重要,認清兩者的優缺點,進而合適的地方使用合適的語言,這才最重要。

在這個指南中,我們將比較 Rust 和 Python 這兩門語言,同時將討論它們各自的應用場景,回顧使用 Rust vs. Python 的優缺點,並解釋 Rust 為什麼越來越受歡迎(甚至可能取代 Python)。

Rust [1] 是一門系統編程語言,專注於安全,尤其是並發安全,支持函數式和命令式以及泛型等編程範式的多範式語言。Rust 在語法上和 C++ 類似,但是設計者想要在保證性能的同時提供更好的內存安全。Rust 最初是由 Mozilla 研究院的 Graydon Hoare 設計創造,然後在 Dave Herman, Brendan Eich 以及很多其他人的貢獻下逐步完善的。Rust 的設計者們通過在研發 Servo 網站瀏覽器布局引擎過程中積累的經驗優化了 Rust 語言和 Rust 編譯器。

Rust 擁有 優秀的文檔 [2] 、友好的編譯器和有用的錯誤消息,以及頂級工具,包括集成包管理器、構建工具、支持自動完成和類型檢查的智能多編輯器、自動格式化程序等等。

Rust 發布於 2010 年。雖然和 Python 相比,Rust 是一門年輕的語言,但是它的社區正在穩步增長。事實上,Rust 已經連續五年(2016,2017,2018,2019,2020)在 Stack Overflow 開發者調查的「最受喜愛編程語言」評選項目中摘取桂冠。

乍一看,Rust 的靜態化和強類型化可能看起來有點極端。但從長遠來看,這有助於防止意外的代碼行為。

Python [3] 是一門旨在幫助開發人員更有效地工作和更有效地集成系統的編程語言。Python 提供了高效的高級數據結構,還能簡單有效地面向對象編程。Python 語法和動態類型,以及解釋型語言的本質,使它成為多數平台上寫腳本和快速開發應用的編程語言,隨著版本的不斷更新和語言新功能的添加,逐漸被用於獨立的、大型項目的開發。如果速度是最重要的,可以使用較低級別的 API 調用,如 CPython [4] 。

1991 年 Guido van Rossum 推出了 Python,以其代碼的可讀性、無分號和花括弧而著稱。

除了可擴展性之外,Python 還是一門解釋型語言,這使得它比大多數編譯型語言要慢。正如您可能期望的那樣,Python 擁有一個龐大的庫生態系統和一個龐大的專業社區。

Rust 被應用於系統開發、操作系統、企業系統、微控制器應用、嵌入式系統、文件系統、瀏覽器組件、虛擬現實的模擬引擎等。

當性能很重要的時候,Rust 是一種常用的語言,因為它能很好地處理大量數據。它可以處理 CPU 密集型的操作,如執行演算法,這就是為什麼 Rust 比 Python 更適合系統開發的原因。

Rust 保證了內存的安全性,讓你可以控制線程行為和線程之間的資源分配方式。這使你能夠構建復雜的系統,也使得 Rust 比 Python 更有優勢。

總而言之,你應在以下情況下使用 Rust:

Python 可以用於許多應用領域,從 Web 開發,到數據科學和分析,到 AI 和機器學習,再到軟體開發。

Python 被廣泛用於機器學習,數據科學和 AI,因為它:

在以下情況下,你應該使用 Python:

考慮到 Rust 的迅速普及、受歡迎程度和廣泛的使用案例,它幾乎不可避免地會在不久的將來超越 Python,以下是一些原因。

Rust 超越 Python 的一個主要原因是性能。因為 Rust 是直接編譯成機器代碼的,所以在你的代碼和計算機之間沒有虛擬機或解釋器。

與 Python 相比,另一個關鍵優勢是 Rust 的線程和內存管理。雖然 Rust 不像 Python 那樣有垃圾回收機制,但 Rust 中的編譯器會強制檢查無效的內存引用泄漏和其他危險或不規則行為。

編譯語言通常比解釋語言要快。但是,使 Rust 處於不同水平的是,它幾乎與 C 和 C ++一樣快,而且沒有額外開銷。

讓我們看一個用 Python 編寫的 O(log n) 程序的示例,並使用迭代方法計算完成任務所需的時間:

輸出:

現在,讓我們來看一下使用迭代方法用 Rust 編寫的定時 O(log n) 程序:

輸出

在沒有使用任何優化技術的情況下,Rust 和 Python 在同一台機器上執行類似的操作分別需要 4.6 微秒和 8.6 微秒。這意味著 Python 花費的時間幾乎是 Rust 的兩倍。

Python 和大多數現代編程語言一樣,被設計成內存安全的。然而,即使沒有垃圾回收。Rust 在內存安全方面卻讓 Python 望塵莫及。

Rust 採用了一種獨特的方式來確保內存安全,其中涉及所有權系統和借用檢查器(borrow checker)。Rust 的借用檢查器確保引用和指針不會超過它們所指向的數據。

Python 和其他語言一樣,提供了錯誤檢查和日誌機制。但是在讓開發者知道哪裡出了什麼問題的時候,Rust 和 Python 之間有一些差異。

舉一個 Python 變數錯誤的典型例子:

Python 輸出

Rust 中的類似示例:

Rust 輸出

在這里,Rust 推薦了可能的變數,這些變數可能是你想輸入的。Python 只會拋出錯誤,而不會給出如何修復的建議。

再舉個例子:

此代碼引發錯誤,因為默認情況下 Rust 中的變數是不可變的。除非它具有關鍵字 mut ,否則無法更改。

錯誤:

修正錯誤:

如你所見,現在它不會引發任何錯誤。除此之外,Rust 不允許不同的數據類型相互操作,除非將它們轉換為相同的類型。

因此,維護 Rust 代碼庫通常很容易。除非指定,否則 Rust 不允許更改。Python 是允許這種性質的更改的。

與大多數編譯語言相比,Rust 因其速度快、內存安全有保證、超強的可靠性、一致性和用戶友好性而備受青睞。在編程中,我們已經到了速度開始變得毫不費力的地步。

隨著技術的發展,它變得越來越快,試圖在更短的時間內做更多的事情,而不需要那麼多的權衡。Rust 幫助實現了這一點,同時又不妨礙開發者的工作。當技術試圖推動可以實現的邊界時,它也會考慮系統的安全性和可靠性,這是 Rust 背後的主要思想。

除了速度外,Python 在並行計算方面也有局限性。

Python 使用全局解釋器鎖(GIL),它鼓勵只有一個線程同時執行,以提高單線程的性能。這是一大局限,因為它意味著你不能使用多個 CPU 核進行密集計算。

如前所述,Stack Overflow 的「 2020 開發人員調查」中有 86%的受訪者將 Rust 稱為 2020 年最喜歡的編程語言。

同樣,「 2020 HackerRank 開發人員技能報告」的受訪者將 Rust 列為他們計劃下一步學習的十大編程語言:

相比之下,2019 年的調查將 Rust 排在列表的底部,這表明 Rust 開發人員社區正在迅速增長。

這些數據表明,Rust 正在成為主流開發者社區的一部分。許多大公司都在使用 Rust,一些開發者甚至用它來構建其他編程語言使用的庫。著名的 Rust 用戶包括 Mozilla、Dropbox、Atlassian、npm 和 Cloudflare 等等。

Amazon Web Service 還對 Lambda,EC2 和 S3 中的性能敏感組件採用了 Rust。在 2019 年,AWS 宣布贊助 Rust 項目,此後為 Rust 提供了 AWS 開發工具包。

公司正越來越多地用更高效的編程語言(如 Rust)取代速度較慢的編程語言。沒有其他語言能像 Rust 一樣在簡單和速度之間做出平衡。

Rust 已經發展成為一門易於使用的編程語言,因此它的使用率有所提高。盡管 Python 在機器學習/數據科學社區中佔有堅實的地位,但 Rust 在未來很可能被用作 Python 庫更有效的後端。

Rust 具有取代 Python 的巨大潛力。目前的趨勢是,在應用程序、性能和速度方面,Rust 不僅僅是一種編程語言,它還是一種思維方式。

各位看官你們覺得呢?評論區留下你的看法!

⑶ 2020-09-26:請問rust中的&和c++中的&有哪些區別

RUST中的&表示引用

  • Rust 有編譯時變數引用檢查

  • &引用的變數默認情況下不可以直接修改(可以使用unsafe{})

  • &mut 引用的變數可以在生命周期內部僅能同時存在至多一個,可以當C/CPP中指針用

  • &/&mut引用的變數會在作用域結束後釋放

  • 如果必須要有多個引用或多個變數實例,可以使用clone()方法

  • 以上特性均為編譯時特性,不影響運行時性能

C/CPP中 &表示變數的內存地址,是偏向底層的,C/CPP沒有編譯時變數檢查,所以比較自由

⑷ 大家如何評價Rust語言

我用rust正在寫一個區塊鏈項目。

如果不熟悉它的機制,很可能會寫得非常啰嗦。
舉個例子Mutex<RefCell<Rc>>> 這種類型多了會讓人崩潰。

c++很多東西被簡化了, 比如拷貝構造函數變成了Copy trait,移動構造函數自帶。 RAII被rust強推(連lock都是).. 想要用內存不安全的操作需要加unsafe。c++那幾個智能指針變成rust的基本類型了。所有的變數都會有一個所有權,不用智能指針的話,只能用引用(rust叫借用),增加了很多限制。指針什麼的很難看到了(寫起來啰嗦)

惡心的生命周期標注,沒有ide很容易被這個煩死。

沒了容器類, 這個習慣c++的要吐槽。

加了很多函數編程的概念。 比如: arr.to_iter().filter(|x| x.age > 20), 還有模式匹配,高階枚舉,但總體沒有scala ocaml這類強大。

完全編譯時, 極少運行時(有類似c++的typeid),要想用類似java的反射機制就不要想了。 泛型和c++一樣, 基本就是一個文本替換(宏)

常用的功能, 如多線程,日誌,文件,網路等都比c++ std和boost好用很多, 但是功能也沒有那麼強大, 不少功能和c一樣直接在系統內核上封裝了一下,寫起來跟c有點像。

完全拋棄面向對象,和go很像,全是struct。這點真心比c++半吊子面向對象強。

比c++方便最多的地方是有一個模塊管理系統,項目的結構都是訂死的(和sbt有點像),靈活性不強。

目前社區不完善,基本上找不到什麼有用的論壇。debug比較痛苦。

總體覺得是c++的閹割版,寫起來很難像c++一樣放得開。小項目會快那麼一點,畢竟不用寫makefile。

⑸ Rust 和 C++ 有哪些優劣

Rust 野心勃勃的想要取代 C++,別人問你們對 Go 怎麼看的時候他們就直接回答我們的目標是 C++,Go 也是個很好的語言balabala。
GitHub Wiki 頁面上有一個簡單的比較 Rust for CXX programmers · rust-lang/rust Wiki · GitHub

最直觀的區別就是 Rust 沒有 C++ 的歷史包袱和 C 包袱,所以一切都能更 clear。還有現代的模塊系統。但如果僅僅如此就僅僅是一個 Better C++。

但是 Rust 有更精細的編譯時檢查,把 C++ 的 RAII 模式進行嚴格的編譯時檢查,做到了編譯時的隱式確定性析構。同時區分了 mut 和非 mut,保護數據的不變性的同時能更適合並發。將類型安全執行到可以達到的極致。(匿名用戶的答案非常棒!)

並且,雖然 C++ 也在不斷吸取函數式特性,但是 Rust 做得更徹底,模式匹配和代數數據類型結合起來的威力誰用誰知道。錯誤處理就用的這種方法,沒有異常,也不像 Go 有額外的返回值。

對泛型的支持很好,而且泛型出錯了編譯器的錯誤信息也很友好,不像 C++ 的模板編程……實際上所有的錯誤信息都很友好,用人話給你說清楚了你遇到了什麼問題,有的時候還能幫你提供修改建議。

同時有模式匹配樣式的宏,在代碼生成的同時保證安全。這個我是聽 @權循真 (upsuper) 說的……他說聲明被一堆宏穿插過來穿插過去,他去 IRC 抱怨別人回復說你去 Servo 組用 Rust 吧,沒有這個問題……

盡管生命期的概念有點費解,但是語言本身的元素並沒有那麼多,不需要學習太多的概念。C++ 中構造函數都有各種細節各種坑我簡直難以想像。只有類似介面的東西,沒有繼承可能對一些人來說很不習慣——但是你真的需要繼承嗎?

Rust 的參與者很多都是資深的 C++ 程序員,是一個很對 C++ 程序員胃口的語言。而且上手也很簡單,常用指針基本都有對應版本。名字空間的 :: 操作符更是熟悉。

⑹ 對比 Go 語言,Rust 有什麼優勢和劣勢

我並沒有什麼編程的經驗,覺得編程實在是太復雜了,不喜歡去研究太多,對這個也不怎麼懂,只能說自己是個半吊子,就是所掌握的知識,也是東拼西湊的,朋友和我說點兒,自己去書上看一點兒,只能說根據自己的體驗給出一些體會吧。


其實我覺得什麼代碼啊編程啊這些東西還是比較適合理工的學生去研究,我一看腦袋就大,完全不明白在講什麼。我大概了解的就是這些,語言的話大家可以多方面的去了解,也不是說有缺點就是不好,看配置看個人吧,每個人習慣不一樣,也許有的人用不穩定的還覺得挺好呢,有的人就喜歡比較完美的,在我看來編程這個東西真的是很復雜,會有很多的代碼,這些代碼弄得我自己頭都大了,有的時候還得去惡補一下。

⑺ 對比 Go 語言,Rust 有什麼優勢和劣勢

Go語言是谷歌2009發布的第二款開源編程語言。Go語言專門針對多處理器系統應用程序的編程進行了優化,使用Go編譯的程序可以媲美C或C++代碼的速度,而且更加安全、支持並行進程。

Rust是Mozilla開發的注重安全、性能和並發性的編程語言。"Rust",由web語言的領軍人物Brendan Eich(js之父),Dave Herman以及Mozilla公司的Graydon Hoare 合力開發。Rust是針對多核體系提出的語言,並且吸收一些其他動態語言的重要特性,比如不需要管理內存,比如不會出現Null指針等等。


不管是GO語言還是ruts都是各有各的長處,各有各的缺點的,每個都有自己存在的意義和用處,可以互不打擾的,選擇適合自己的語言去使用,讓他發揮到自己的用處才是他所存在的意義,也不能太過於可以的去比較他們之間互相的好與壞。

⑻ [from js to rust 系列][宏-01][官網文檔 19.5]高級特性:宏[譯文]

原文鏈接:The Rust Programming Language

作者:rust 團隊

譯文首發鏈接:zhuanlan.hu.com/p/516660154

著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請註明出處。

中間加了一些對於 JavaScript 開發者有幫助的註解。在學習 Rust 的代碼時,尤其是有一些經驗的開發者,一般都會去看一些相對簡單的庫,或者項目去學習。Rust 的原生語法本身並不復雜,有一定的 TypeScript 經驗的開發者,應該通過看一些教程,都能開始熟悉基本的語法。反而是宏相關內容,雖然就像本文寫的,大部分開發者不需要自己去開發宏,但是大家使用宏,和看懂別人代碼的主體邏輯,幾乎是繞不開宏的。雖然宏在 rust 里屬於「高級」內容,但是因為其和 rust 本身語法的正交性,Hugo 認為,反而應該早一些學習。並且,如果一個 JavaScript 開發者之前接觸過代碼生成器、babel,對這一章的內容反而會比較親切。

Derive 宏、attribute 宏特別像 rust 里的裝飾器。從作用上,和一般庫提供的介面來看,也特別像。所以如果之前有裝飾器的經驗的開發者,對這一章節應該也會比較親切。

我們一個個來討論這些內容,但是首先,我們看既然我們已經有了函數,我們為什麼需要這些特性。

基本上,宏是指一些代碼可以生成另一些代碼,這一塊的技術一般稱為元編程(Hugo 註:代碼生成器也屬於這一類技術)。在附錄 C,我們討論了 derive 屬性,可以幫助你生成一系列的 trait。整本書我們也在用 println! 和 vec! 宏。這些宏在編譯時,都會展開成為代碼,這樣你就不需要手寫這些代碼。

元編程可以幫助你減少手寫和維護的代碼量,當然,函數也能幫助你實現類似的功能。但是,宏有函數沒有的威力。

宏不好的地方在於,宏很復雜,因為你要用 rust 代碼寫 rust 代碼(Hugo 註:任何元編程都不是簡單的事兒,包括 JS 里的。)。因為這種間接性,宏的代碼要更難讀、難理解、難維護。(Hugo 註:個人學 rust,感覺最難不是生命周期,因為生命周期的問題,可以通過用一些庫繞過去,或者無腦 clone,如果是應用程序,則可以通過使用 orm 和資料庫來繞過很多生命周期的問題。反而是宏,因為稍微有點規模的代碼的,都有一大堆宏。宏最難的不是語法,而是作者的意圖,因為本質是他造了一套 DSL)

另一個和函數不一樣的地方是,宏需要先定義或者引入作用域,而函數可以在任何地方定義和使用。

我們可以通過 vec! 宏來創建任意類型的 vector,例如 2 個 integer,或者五個 string slice.

一個簡化的 vec! 宏:

#[macro_export] 標注指明了這個宏在 crate 的作用域里可用。沒有這個標注,宏不會被帶入到作用域里。

macro_rules! 後面就是宏的名字。這里只有一種模式匹配的邊(arm):( (( (x:expr ),* ) ,=> 後面是這個模式對應要生成的代碼。如果這個模式匹配成功,對應的代碼和輸入的參數組成的代碼就會生成在最終的代碼中。因為這里只有一種邊,所以只有這一種可以匹配的條件。不符合這個條件的輸入,都會報錯。一般復雜的宏,都會有多個邊。

這里匹配的規則和 match 是不一樣的,因為這里的語法匹配的是 rust 的語法,而不是 rust 的類型,或者值。更全的宏匹配語法,見文檔。

對於 宏的輸入條件 ( (( (x:expr ),* ),()內部是匹配的語法,expr表示所有Rust的表達式。() 內部是匹配的語法,expr 表示所有 Rust 的表達式。()內部是匹配的語法,expr表示所有Rust的表達式。() 後面的都喊表示這個變數後面有可能有逗號,* 表示前面的模式會出現一次或者多次。(Hugo 註:像不像正則?宏語法其實挺簡單的,不要被高級唬住了。當然,宏還是難的,宏要考慮的問題本身是一個復雜的問題。)

當我們調用:vec![1, 2, 3]; 時,$x 模式會匹配 3 個表達式 1 , 2 和 3。

現在我們看一下和這個邊匹配的生成代碼的部分:

在 ()里的tempvec.push(() 里的 temp_vec.push(()里的tempvec.push(x); 就是生成的代碼的部分。* 號仍然表示生成零個和多個,這個匹配的具體個數,要看匹配條件命中的個數。

你傳任意參數,最後就生成符合上面條件的代碼。

雖然過程宏有三種:custom derive、attribute-like 和 function-like,但是原理都是一樣的。

如果要創建過程宏,定義的部分需要在自己的 crate 里,並且要定義特殊的 crate 類型。(Hugo 註:相當於定義了一個 babel 插件,只不過有一套 rust 自己的體系。這些宏會在編譯的時候,按照書寫的規則,轉成對應的代碼。所有的宏,都是代碼生成的手段,輸入是代碼,輸入是代碼。)這種設計,我們有可能會在未來消除。

下面是一個過程宏的例子:

過程宏接收一個 TokenStream,輸出一個 TokenStream。TokenStream 類型定義在 proc_macro 里,表示一系列的 tokens。這個就是這種宏的核心機制,輸入的代碼(會被 rust) 轉成 TokenStream,然後做一些按照業務邏輯的操作,最後生成 TokenStream。這個函數也可以疊加其他的屬性宏(#[some_attribute], 看起來像裝飾器的邏輯,也可以理解為一種鏈式調用),可以在一個 crate 里定義多個過程。(Hugo 註:搞過 babel 的同學肯定很熟悉,一樣的味道。沒搞過的同學,強烈建議先學學 babel。)

下面我們來看看不同類型的過程宏。首先從自定義 derive 宏開始,然後我們介紹這種宏和其他幾種的區別。

我們創建一個 crate 名字叫 hello_macro,定義一個 HelloMacro 的 trait,關聯的函數名字叫 hello_macro。通過使用這個宏,用戶的結構可以直接獲得默認定義的 hello_macro 函數,而不需要實現這個 trait。默認的 hello_macro 可以列印 Hello, Macro! My name is TypeName!,其中 TypeName 是實現這個 derive 宏的結構的類型名稱。

創建這個宏的過程如下,首先

然後定義 HelloMacro trait

這樣我們就有了一個 trait,和這個triat 的函數。用戶可以通過這個 trait 直接實現對應的函數。

但是,用戶需要每次都實現一遍 hello_macro。如果 hello_macro 的實現都差不多,就可以通過 derive 宏來是實現。

因為 Rust 沒有反射機制,我們不可以在執行時知道對應類型的名字。我們需要在編譯時生成對應的代碼。

下一步,定義過程宏。在這個文章編寫時,過程宏需要在自己的 crates 里。最終,這個設計可能改變。關於 宏 crate 的約定是:對於一個名為 foo 的 crate,自定義 drive 宏的crate 名字為 foo_derive。我們在 hello_macro 項目中創建 hello_macro_derive crate。

我們的兩個的 crate 關聯緊密,所以我們在 hello_macro crate 里創建這個 crate。如果我們要改變 hello_macro 的定義,我們同樣也要更改 hello_macro_derive 的定義。這兩個 crates 要隔離發布。當用戶使用時,要同時添加這兩個依賴。為了簡化依賴,我們可以讓 hello_macro 使用 hello_macro_derive 作為依賴,然後導出這個依賴。但是,這樣,如果用戶不想使用 hello_macro_derive,也會自動添加上這個依賴。

下面開始創建 hello_macro_derive,作為一個過程宏 crate。需要添加依賴 syn 和 quote。下面是這個 crate 的 Cargo.toml。

在 lib.rs 里添加下述代碼。注意,這個代碼如果不增加 impl_hello_macro 的實現是通不過編譯的。

注意,這里把代碼分散成兩部分,一部分在 hello_macro_derive 函數里,這個函數主要負責處理 TokenStream,另一部分在 impl_hello_macro,這里負責轉換語法樹:這樣編寫過程宏可以簡單一些。在絕大部分過程宏立,對於前者的過程一般都是一樣的。一般來說,真正的區別在 impl_hello_macro,這里的邏輯一般是一個過程宏的業務決定的。

我們引入了三個 crates: proc_macro, syn 和 quote。proc_macro 內置在 rust 立,不需要在 Cargo.toml 中引入。proc_macro 實際是 rust 編譯器的一個介面,用來讀取和操作 Rust 代碼。

syn crate 把 Rust 代碼從字元串轉換為可以操作的結構體。quote crate 把 syn 數據在轉回 Rust 代碼。這些 Crate 可以極大簡化過程宏的編寫:寫一個 Rust 代碼的 full parser 可不是容易的事兒!

當在一個類型上標注 [derive(HelloMacro)] 時,會調用 hello_macro_derive 函數。之所以會有這樣的行為,是因為在定義 hello_macro_derive 時,標注了 #[proc_macro_derive(HelloMacro)] 在函數前面。

hello_macro_derive 會把輸入從 TokenStream 轉換為一個我們可以操作的數據結構。這就是為什麼需要引入 syn 。sync 的 parse 函數會把 TokenStream 轉換為 DeriveInput。

上述這個結構的意思是:正在處理的是 ident(identifier, 意味著名字)為 Pancakes 的 unit struct。其他的欄位表示其餘的 Rust 代碼。如果想了解更詳細的內容,請參考。

接下來,我們就要開始定義 impl_hello_macro。這個函數實現了添加到 Rust 代碼上的函數。在我們做之前,注意 derive macro 的輸出也是 TokenStream。返回的 TokenStream 就是添加完代碼以後的代碼。當編譯 crate 時,最終的代碼,就是處理完成的代碼了。

你也許也會發現,這里調用 syn::parse 時使用了 unwrap,如果報錯就中斷。這里必須這么做,因為最終返回的是 TokenStream,而不是 Result。這里是為了簡化代碼說明這個問題。在生產代碼,你應該處理好報錯,提供更詳細的報錯信息,例如使用 panic! 或者 expect。

下面是代碼:

通過上面的代碼,cargo build 就可以正常工作了。如果要使用這個代碼,需要把兩個依賴都加上。

現在執行下面的代碼,就可以看到 Hello, Macro! My name is Pancakes!

下一步,我們來 探索 其他類型的過程宏。

屬性宏和 derive 宏類似,但是可以創造除了 derive 意外的屬性。derive 只能作用於 structs 和 enums,屬性宏可以作用於其他的東西,比如函數。下面是一個屬性宏的例子:例如你製作了一個名為 route 的屬性宏來在一個web 框架中標注函數。

#[route] 是框架定義的過程宏。定義這個宏的函數類似:

這里,有兩個參數,類型都是 TokenStream。第一個是屬性的內容,GET, "/" 部分,第二個是標注屬性宏傳入的語法部分,在這個例子里,就是剩下的 fn index() {}。

工作原理和 derive 宏是一樣的。

函數宏的使用比較像調用一個 rust 函數。函數宏有點像 macro_rules! ,能提供比函數更高的靈活性。例如,可以接受未知個數的參數。但是,macro_rules! 只能使用在上述章節的匹配型的語法。而函數宏接受 TokenStream 參數作為入參,和其他過程宏一樣,可以做任何變換,然後返回 TokenStream。下面是一個函數宏 sql

這個宏接受 SQL 語句,可以檢查這個 SQL 的語法是否正確,這種功能比 macro_rules! 提供的要復雜的多。這個 sql! 的宏可以定義為:

這個定義和自定義 derive 宏類似:接受括弧內的 tokens,返回生成的代碼。

好了,現在你有了一些可能不常用的 Rust 新工具,但是你要知道的是,在需要的場合,他們的運行原理是什麼。我們介紹了一些復雜的話題,當你在錯誤處理或者別人的代碼里看到這些宏時,可以認出這些概念和語法。可以使用這一章的內容作為解決這些問題的索引。

⑼ rust性能到底有多好

這個和c++大同小異。 因為把大量的運行時放到了編譯時。 只不過編譯器優化不夠還達不到c++性能。
其實rust和c++的關系很想scala和java的關系。 rust和c++很多概念甚至庫都是通用的,抽象化方式,比如泛性,多態,可變性,拷貝構造,移動構造,都是一樣的。 你要是寫過c++再來寫rust,就會吐槽原來這個功能也有啊...

運行速度上rust c++ java都是大同小異,io處理上 java甚至比rust還要快。內存消耗rust和c++是一個級別的,都非常低。 另外很多人寫rust喜歡用arc cellref這些只能指針, 其實是給這些變數增加了動態性會導致額外開銷,所以這些特性用多了後,rust性能也不會太高:

⑽ Rust編程語言的自動特性

特性對象中有一部分特性是自動特性,rust里的自動特性有Send Sync Unpin UnwindSafe RefUnwindSafe。

自動特性的自動就是說當代碼在需要任何這五個特性的時候如果沒有寫上,編譯器會根據一些規格給加上。其實也就是一些推導的過程,並不復雜。

當引用一個類型,引用可變類型,解構常量類型,解構可變類型,數組類型重復表達式中的類型,切片類型等這些類型實現了任意以上五個特性,這些類型就會自動具有這些特性。

函數項類型包括函數名稱,類型參數,早期生命時間參數等能夠標識這個函數的所有部分,以及泛型參數。

結構,枚舉,聯合,元祖類型只有所有的欄位都實現了自動特性它們就具有自動特性。

閉包只有所有值和共享引用的類型實現了自動特性它就具有自動特性。

對於泛型如果類型參數實現Sync特性,編譯器會為這個引用類型實現Send,如果類型是Send則編譯器不會去實現Send。

自動特性可以有負實現,就是實現的時候在特性前面加上!號。意思就是類型實現了自動特性引用或者解構的可變及不可變反而是去除了這個自動特性。

熱點內容
個人網站模板源碼 發布:2025-05-18 02:51:17 瀏覽:489
主伺服器ip地址 發布:2025-05-18 02:46:29 瀏覽:854
電腦配置太低玩不了絕地求生怎麼辦 發布:2025-05-18 02:38:39 瀏覽:796
存儲過程怎麼出錯了 發布:2025-05-18 02:37:16 瀏覽:367
32寸演算法 發布:2025-05-18 02:22:14 瀏覽:743
寶塔資料庫備份 發布:2025-05-18 02:14:18 瀏覽:192
安卓商店下載的光遇是什麼服 發布:2025-05-18 02:13:38 瀏覽:31
網頁挖礦源碼 發布:2025-05-18 02:13:34 瀏覽:307
centosftp伺服器設置參數 發布:2025-05-18 02:12:55 瀏覽:216
賬號密碼保存在瀏覽器哪裡 發布:2025-05-18 01:56:43 瀏覽:833