A. rust里的rustangelo怎麼用
燒錄卡是不能直接播放音樂的,題主你只能用多媒體程序播放音樂
建議網路moonshl2這個,是專門用於在NDS燒錄卡上播放音樂的
B. rust是前端還是後端
後端。
Rust是一款高級通用語言,而且屬於少有的一款兼顧開發和執行效率的編程語言。Rust結合了腳本語言的語法結構和C語言編譯執行效率,並且具有類似垃圾回收和數據類型及所有權系統等功能,所以可靠性和高性能運行都屬於Rust的特色。
雖然是一個非常年輕的編程語言,但是Rust可以算是最近幾年最流行的編程語言。5月發布的Stack Overflow 2020開發者調查中,Rust被86.1%開發者選擇為「最喜歡」的編程語言,比第二名TypeScript高出近20%。
雖然Rust並不是一個專屬的網路應用開發語言,但是作為一個以安全著稱的編輯語言,實際上是非常適合網路開發的。而且因為是編譯型語言,編譯器也能在過程中就安全穩定的問題作出提醒,作為後端網路開發還是不錯的一個優勢。
Rust的通用庫中已經包含了類似TcpListener這樣的網路通訊庫,可以直接通過調用std : : net 下面的TcpListener來直接監聽Tcp埠,然後再處理Request。這點上與一些腳本型的編程語言比要自由得很多。
Rust作為比較流行的編程語言,也有不少第三方HTTP庫來支持Web開發,可以不用再花時間從底層開發,比較熱門的庫像Hyper或者Tide都是被不少Web開發框架用到的。Rust下Web開發框架也不少,比較熱門的有Rocket、Actix-Web、Tower-web、Warp等等框架。
C. Rust工作台怎麼使用
對准它然後按E 接下來就把裝備放進去,修東西,會需要一些材料。
D. rust可以開發分布式系統嗎
rust是可以開發分布式系統的。
引子
構建一個分布式系統 並不是一件容易的事情,我們需要考慮很多的問題,首先就是我們的系統到底需要提供什麼樣的功能,譬如:
一致性:我們是否需要保證整個系統的線性一致性,還是能容忍短時間的數據不一致,只支持最終一致性。
穩定性:我們能否保證系統 7 x 24 小時穩定運行。系統的可用性是 4 個 9,還有 5 個 9?如果出現了機器損壞等災難情況,系統能否做的自動恢復。
擴展性:當數據持續增多,能否通過添加機器就自動做到數據再次平衡,並且不影響外部服務。
分布式事務:是否需要提供分布式事務支持,事務隔離等級需要支持到什麼程度。
上面的問題在系統設計之初,就需要考慮好,作為整個系統的設計目標。為了實現這些特性,我們就需要考慮到底採用哪一種實現方案,取捨各個方面的利弊等。
後面,我將以我們開發的分布式 Key-Value TiKV 作為實際例子,來說明下我們是如何取捨並實現的。
TiKV
TiKV 是一個分布式 Key-Value store,它使用 Rust 開發,採用 Raft 一致性協議保證數據的強一致性,以及穩定性,同時通過 Raft 的 Configuration Change 機制實現了系統的可擴展性。
TiKV 提供了基本的 KV API 支持,也就是通常的 Get,Set,Delete,Scan 這樣的 API。TiKV 也提供了支持 ACID 事務的 Transaction API,我們可以使用 Begin 開啟一個事務,在事務裡面對 Key 進行操作,最後再用 Commit 提交一個事務,TiKV 支持 SI 以及 SSI 事務隔離級別,用來滿足用戶的不同業務場景。
Rust
在規劃好 TiKV 的特性之後,我們就要開始進行 TiKV 的開發。這時候,我們面臨的第一個問題就是採用什麼樣的語言進行開發。當時,擺在我們眼前的有幾個選擇:
Go,Go 是我們團隊最擅長的一門語言,而且 Go 提供的 goroutine,channel 這些機制,天生的適合大規模分布式系統的開發,但靈活方便的同時也有一些甜蜜的負擔,首先就是 GC,雖然現在 Go 的 GC 越來越完善,但總歸會有短暫的卡頓,另外 goroutine 的調度也會有切換開銷,這些都可能會造成請求的延遲增高。
java,現在世面上面有太多基於 Java 做的分布式系統了,但 Java 一樣有 GC 等開銷問題,同時我們團隊在 Java 上面沒有任何開發經驗,所以沒有採用。
C++,C++ 可以認為是開發高性能系統的代名詞,但我們團隊沒有特別多的同學能熟練掌握 C++,所以開發大型 C++ 項目並不是一件非常容易的事情。雖然使用現代 C++ 的編程方式能大量減少 data race,dangling pointer 等風險,我們仍然可能犯錯。
當我們排除了上面幾種主流語言之後,我們發現,為了開發 TiKV,我們需要這門語言具有如下特性:
靜態語言,這樣才能最大限度的保證運行性能。
無 GC,完全手動控制內存。
Memory safe,盡量避免 dangling pointer,memory leak 等問題。
Thread safe,不會遇到 data race 等問題。
包管理,我們可以非常方便的使用第三方庫。
高效的 C 綁定,因為我們還可能使用一些 C library,所以跟 C 交互不能有開銷。
綜上,我們決定使用 Rust,Rust 是一門系統編程語言,它提供了我們上面想要的語言特性,但選擇 Rust 對我們來說也是很有風險的,主要有兩點:
我們團隊沒有任何 Rust 開發經驗,全部都需要花時間學習 Rust,而偏偏 Rust 有一個非常陡峭的學習曲線。
基礎網路庫的缺失,雖然那個時候 Rust 已經出了 1.0,但我們發現很多基礎庫都沒有,譬如在網路庫上面只有 mio,沒有好用的 RPC 框架,HTTP 也不成熟。
但我們還是決定使用 Rust,對於第一點,我們團隊花了將近一個月的時間來學習 Rust,跟 Rust 編譯器作斗爭,而對於第二點,我們就完全開始自己寫。
幸運的,當我們越過 Rust 那段陣痛期之後,發現用 Rust 開發 TiKV 異常的高效,這也就是為啥我們能在短時間開發出 TiKV 並在生產環境中上線的原因。
一致性協議
對於分布式系統來說,CAP 是一個不得不考慮的問題,因為 P 也就是 Partition Tolerance 是一定存在的,所以我們就要考慮到底是選擇 C - Consistency 還是 A - Availability。
我們在設計 TiKV 的時候就決定 - 完全保證數據安全性,所以自然就會選擇 C,但其實我們並沒有完全放棄 A,因為多數時候,畢竟斷網,機器停電不會特別頻繁,我們只需要保證 HA - High Availability,也就是 4 個 9 或者 5 個 9 的可用性就可以了。
既然選擇了 C,我們下一個就考慮的是選用哪一種分布式一致性演算法,現在流行的無非就是 Paxos 或者 Raft,而 Raft 因為簡單,容易理解,以及有很多現成的開源庫可以參考,自然就成了我們的首要選擇。
在 Raft 的實現上,我們直接參考的 etcd 的 Raft。etcd 已經被大量的公司在生產環境中使用,所以它的 Raft 庫質量是很有保障的。雖然 etcd 是用 Go 實現的,但它的 Raft library 是類似 C 的實現,所以非常便於我們用 Rust 直接翻譯。在翻譯的過程中,我們也給 etcd 的 Raft fix 了一些 bug,添加了一些功能,讓其變得更加健壯和易用。
現在 Raft 的代碼仍然在 TiKV 工程裡面,但我們很快會將獨立出去,變成獨立的 library,這樣大家就能在自己的 Rust 項目中使用 Raft 了。
使用 Raft 不光能保證數據的一致性,也可以藉助 Raft 的 Configuration Change 機制實現系統的水平擴展,這個我們會在後面的文章中詳細的說明。
存儲引擎
選擇了分布式一致性協議,下一個就要考慮數據存儲的問題了。在 TiKV 裡面,我們會存儲 Raft log,然後也會將 Raft log 裡面實際的客戶請求應用到狀態機裡面。
首先來看狀態機,因為它會存放用戶的實際數據,而這些數據完全可能是隨機的 key - value,為了高效的處理隨機的數據插入,自然我們就考慮使用現在通用的 LSM Tree 模型。而在這種模型下,RocksDB 可以認為是現階段最優的一個選擇。
RocksDB 是 Facebook 團隊在 LevelDB 的基礎上面做的高性能 Key-Value Storage,它提供了很多配置選項,能讓大家根據不同的硬體環境去調優。這里有一個梗,說的是因為 RocksDB 配置太多,以至於連 RocksDB team 的同學都不清楚所有配置的意義。
關於我們在 TiKV 中如何使用,優化 RocksDB,以及給 RocksDB 添加功能,fix bug 這些,我們會在後面文章中詳細說明。
而對於 Raft Log,因為任意 Log 的 index 是完全單調遞增的,譬如 Log 1,那麼下一個 Log 一定是 Log 2,所以 Log 的插入可以認為是順序插入。這種的,最通常的做法就是自己寫一個 Segment File,但現在我們仍然使用的是 RocksDB,因為 RocksDB 對於順序寫入也有非常高的性能,也能滿足我們的需求。但我們不排除後面使用自己的引擎。
因為 RocksDB 提供了 C API,所以可以直接在 Rust 裡面使用,大家也可以在自己的 Rust 項目裡面通過 rust-rocksdb 這個庫來使用 RocksDB。
分布式事務
要支持分布式事務,首先要解決的就是分布式系統時間的問題,也就是我們用什麼來標識不同事務的順序。通常有幾種做法:
TrueTime,TrueTime 是 Google Spanner 使用的方式,不過它需要硬體 GPS + 原子鍾支持,而且 Spanner 並沒有在論文裡面詳細說明硬體環境是如何搭建的,外面要自己實現難度比較大。
HLC,HLC 是一種混合邏輯時鍾,它使用 Physical Time 和 Logical Clock 來確定事件的先後順序,HLC 已經在一些應用中使用,但 HLC 依賴 NTP,如果 NTP 精度誤差比較大,很可能會影響 commit wait time。
TSO,TSO 是一個全局授時器,它直接使用一個單點服務來分配時間。TSO 的方式很簡單,但會有單點故障問題,單點也可能會有性能問題。
TiKV 採用了 TSO 的方式進行全局授時,主要是為了簡單。至於單點故障問題,我們通過 Raft 做到了自動 fallover 處理。而對於單點性能問題,TiKV 主要針對的是 PB 以及 PB 以下級別的中小規模集群,所以在性能上面只要能保證每秒百萬級別的時間分配就可以了,而網路延遲上面,TiKV 並沒有全球跨 IDC 的需求,在單 IDC 或者同城 IDC 情況下,網路速度都很快,即使是異地 IDC,也因為有專線不會有太大的延遲。
解決了時間問題,下一個問題就是我們採用何種的分布式事務演算法,最通常的就是使用 2 PC,但通常的 2 PC 演算法在一些極端情況下面會有問題,所以業界要不通過 Paxos,要不就是使用 3 PC 等演算法。在這里,TiKV 參考 Percolator,使用了另一種增強版的 2 PC 演算法。
這里先簡單介紹下 Percolator 的分布式事務演算法,Percolator 使用了樂觀鎖,也就是會先緩存事務要修改的數據,然後在 Commit 提交的時候,對要更改的數據進行加鎖處理,然後再更新。採用樂觀鎖的好處在於對於很多場景能提高整個系統的並發處理能力,但在沖突嚴重的情況下反而沒有悲觀鎖高效。
對於要修改的一行數據,Percolator 會有三個欄位與之對應,Lock,Write 和 Data:
Lock,就是要修改數據的實際 lock,在一個 Percolator 事務裡面,有一個 primary key,還有其它 secondary keys, 只有 primary key 先加鎖成功,我們才會再去嘗試加鎖後續的 secondary keys。
Write,保存的是數據實際提交寫入的 commit timestamp,當一個事務提交成功之後,我們就會將對應的修改行的 commit timestamp 寫入到 Write 上面。
Data,保存實際行的數據。
當事務開始的時候,我們會首先得到一個 start timestamp,然後再去獲取要修改行的數據,在 Get 的時候,如果這行數據上面已經有 Lock 了,那麼就可能終止當前事務,或者嘗試清理 Lock。
當我們要提交事務的時候,先得到 commit timestamp,會有兩個階段:
Prewrite:先嘗試給 primary key 加鎖,然後嘗試給 second keys 加鎖。如果對應 key 上面已經有 Lock,或者在 start timestamp 之後,Write 上面已經有新的寫入,Prewrite 就會失敗,我們就會終止這次事務。在加鎖的時候,我們也會順帶將數據寫入到 Data 上面。
Commit:當所有涉及的數據都加鎖成功之後,我們就可以提交 primay key,這時候會先判斷之前加的 Lock 是否還在,如果還在,則刪掉 Lock,將 commit timestamp 寫入到 Write。當 primary key 提交成功之後,我們就可以非同步提交 second keys,我們不用在乎 primary keys 是否能提交成功,即使失敗了,也有機制能保證數據被正常提交。
E. 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函數式編程的思維。我但願這種遞歸式的做法不會有溢出。
F. 為什麼Rust這樣的語言還需要C/C++編譯器
需要用 VS 的鏈接器,應該是需要支持 FFI 的原因。Windows 是 Rust 承諾要支持的主要平台之一,這意味著,Rust要有能力和Windows上的靜態庫和動態庫來交互。這時候,MSVC的ABI就是繞不過去的一個問題。可以參考 Rustup 的官方文檔對Windows平台的描述,rust-lang-nursery/rustup.rs。 Windows平台上有兩套ABI,一個是MSVC,一個是MinGW。所以你其實有兩個選擇,要麼安裝 msvc,然後
rustup install stable-x86_64-pc-windows-msvc
要麼安裝 MinGW,然後
rustup install stable-x86_64-pc-windows-gnu
在 Rustup 的文檔上有這么一句話 By default rustup on Windows configures Rust to target the 32-bit MSVC ABI, that is the i686-pc-windows-msvc target triple. 所以,默認安裝的話,你需要安裝 msvc。
G. Rust 語法很醜陋嗎如果是,為什麼醜陋呢為什麼設計成這樣呢
Rust 的縮寫是完全可以接受的嘛,也不是非常多,fn mod 這些都是很正常的縮寫嘛,之前看到有人說 Rust 醜陋,應該是說指針語法繁雜,現在指針語法已經被統一和消除了(類型上來說貌似只有 & 是特殊的語法),一般使用是不會遇到讓人厭惡的情景的。
This release also marks the complete removal of the `~` and `@` syntax in favor of library types `Box` and `Gc`.
- 0.11 changelog
原本語法中有 ~ 和 @ 前者是最普通的指針,後者是垃圾回收指針。
於是愈發會有奇葩的凌亂感,比如說
let foobar = 42i
&'a~@foobar // foobar 前面的都是指針
現在這些語法被范型代替了:
聲明:
let foobar = 42i
&'a box Gc::new(foobar)
類型
&'a Box<Gc<int>>
雖然變長了但是更統一了。
當然我是從 0.11 開始學的,前面版本的語法我沒用過,可能弄錯。
題外話:
或許 Rust 是一個參與度最高的語言。
曾經有人評價,一個工程項目一般是:一開始是簡潔但是不完善的,在第二次設計的時候是繁雜並完善的,進行了第三次設計才能變成簡潔但是完善的。
我覺得 Rust 是在進行一個飛快的試錯過程。Rust 在快速的迭代中不斷的試錯,變得復雜然後變得簡單,每一個人都可以參與到語言的設計中去,只要你有干貨輔佐你的觀點,那麼你只用寫在 issues 裡面就行了。Pull Requests · rust-lang/rfcs · GitHub
最重要的是在 1.0 發布之前,你的好想法都不會因為兼容性而被拒絕,也就是說沒有任何兼容性包袱(這就是為什麼 Rust 一直給人語法不穩定的印象),同時已經有不少人和不少項目正在用 Rust 書寫,Rust 編譯器本身和 Servo 都是非常龐大的項目,有幾十萬行級別的代碼吧,語言中如果有什麼常見的坑那麼必然會被填平,我覺得或許會成為一個坑很少的語言。基本上我的代碼編譯通過了就不會有問題。
使用中遇到了坑請提交 iusses。