柚子快報(bào)邀請(qǐng)碼778899分享:Rust學(xué)習(xí)筆記(一)
柚子快報(bào)邀請(qǐng)碼778899分享:Rust學(xué)習(xí)筆記(一)
參考文獻(xiàn)1:https://course.rs/first-try/installation.html
參考文獻(xiàn)2:rust-based-os-comp2024/2024-spring-scheduling-1.md at main · LearningOS/rust-based-os-comp2024 · GitHub
使用wsl2 ubuntu 20.04,照著教程直接
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
?然后無(wú)腦回車安裝完成。
基礎(chǔ)1 牛刀小試
cargo new
cargo run # 直接編譯運(yùn)行,可添加--release生成高性能代碼
cargo build # 先編譯,可添加--release生成高性能代碼
./target/debug/
cargo check # 在不編譯的情況下快速檢查能否編譯通過(guò)【神器】,但比Go差太遠(yuǎn)
Cargo.toml:項(xiàng)目描述,跟package.xml差不多。
依賴直接以如下形式給出:
[dependencies]
rand = "0.3" # 官方包,只需指定版本
hammer = { version = "0.5.0"} # 指定版本的另一種方法
color = { git = "https://github.com/bjz/color-rs" } # 網(wǎng)上的包,指定url
geometry = { path = "crates/geometry" } # 本地的包,指定文件路徑
Cargo.lock:根據(jù)toml生成的項(xiàng)目以來(lái)詳細(xì)清單,基本不需要手動(dòng)修改。
基礎(chǔ)2 Rust入門(亂序筆記,建議跳過(guò))
let綁定的變量不可變,let mut的可變,并且可以一個(gè)let賦多個(gè)值。
我秉承實(shí)用主義解釋方法,rust的話術(shù)后面深入了解之后再接受。
let {mut}
let ({mut} , {mut} , ...): (
這多個(gè)值實(shí)際上構(gòu)成了一個(gè)復(fù)雜變量,但可以單獨(dú)使用。這種隨地湊出向量的搞法比較優(yōu)雅。
下劃線開頭的變量不會(huì)因?yàn)槲词褂枚痪妗?/p>
定義成mut但沒(méi)變過(guò)的變量會(huì)被警告。
非常古典的類型名,i32、f64這種。
后定義的同名變量會(huì)把之前的覆蓋,且不會(huì)報(bào)警。
數(shù)字可以在后面加上下劃線(也可以不加)+類型規(guī)定為對(duì)應(yīng)類型的數(shù)字。
序列用法:
1..5 // 代表1~4
1..=5 // 代表1~5
// 字符也可以用
fn func(var: type, ...) {
let x = {
let y = 3;
y * 2
};
x + 3
}
不加分號(hào)的表達(dá)式會(huì)返回一個(gè)值,可以作為函數(shù)返回值,也可以作為語(yǔ)塊表達(dá)式返回值。
if-else語(yǔ)塊也可以返回值,可以用來(lái)賦值。
Rust也可以用return,只不過(guò)可以不用。
let x = 5;
let y = x; // 直接拷貝值
let x = String::from("Hello"); // 堆上分配內(nèi)存,創(chuàng)建String對(duì)象
let y = x; // 把x綁定的值的所有權(quán)移交給y,x被廢黜
let x: &str = "Hello"; // 字符串字面量只讀,放在靜態(tài)數(shù)據(jù)區(qū)域,此處的x只是一個(gè)引用
let y = x; // 相當(dāng)于x是一個(gè)指針,然后指針值拷貝給了x
基本類型注意:
let a = [1, 1, 4, 5, 1, 4]; // 用.len()獲取數(shù)組長(zhǎng)度
let b = &a[2..4]; // 切片以引用的方式獲取,注意左開右閉。左右界限均可以省略。
let c = (1, 1, 4, 5); // 用c.2獲取第3個(gè)數(shù)
let v = vec![10, 20, 30, 40]; // 宏定義向量
for e in v.iter_mut() { // 要允許值可變,要用iter_mut()
*e *= 2; // 用的是引用值,要用*解引用?
}
v.iter().map(|element| { // 對(duì)迭代器中每個(gè)元素應(yīng)用一個(gè)匿名函數(shù)
element << 1
}).collect() // 將迭代器的結(jié)果收集起來(lái)組成新的集合
fn main() {
let vec0 = Vec::new();
let mut vec1 = fill_vec(vec0);
println!("{} has length {}, with contents: `{:?}`", "vec0", vec0.len(), vec0);
vec1.push(88);
println!("{} has length {}, with contents `{:?}`", "vec1", vec1.len(), vec1);
}
fn fill_vec(vec: Vec
// 這里把vec0的數(shù)據(jù)借調(diào)了,所以vec0訪問(wèn)不了。所以上面的fill_vec得是深拷貝。
let mut vec = vec;
vec.push(22);
vec.push(44);
vec.push(66);
vec
}
結(jié)構(gòu)體的clone方法需要額外#[derive(Clone)]才能使用。
用impl <結(jié)構(gòu)體名>來(lái)定義方法。
需要用&的情景:
借用數(shù)據(jù),實(shí)際上相當(dāng)于取地址。要使用數(shù)據(jù)(比如算術(shù)運(yùn)算)時(shí)需要加*解引用,但是rust居然會(huì)自己解引用。感覺這個(gè)設(shè)計(jì)非常nt,落了下乘。也許我后面會(huì)理解的吧。避免數(shù)據(jù)復(fù)制(傳遞復(fù)雜數(shù)據(jù)結(jié)構(gòu)的引用)共享不可變數(shù)據(jù)可變引用&mut:每個(gè)數(shù)據(jù)同一時(shí)間只能有一個(gè)&mut存在,而且被引用的必須是mut變量。參數(shù)中用&可以避免擁有數(shù)據(jù)所有權(quán),特別是在只需要讀取的時(shí)候。其實(shí)還是傳地址,只是rust會(huì)自己解引用。
rust的枚舉類型可以定義:
結(jié)構(gòu)體變體,即不加struct前綴的三種結(jié)構(gòu)體方法,跟struct類似,要用impl在外面定義用Nested(<其他枚舉類型>)進(jìn)行嵌套枚舉
match的標(biāo)準(zhǔn)用法:
fn process_message(msg: Message) {
match msg {
Message::Quit => println!("Quit message"),
Message::ChangeColor(r, g, b) => {
println!("Change color to RGB({}, {}, {})", r, g, b);
}
Message::Move { x, y } => {
println!("Move to ({}, {})", x, y);
}
}
}
let a = "test";
let b = a.to_string(); // 字符串字面量轉(zhuǎn)String
let a = String::from("test");
let b = &a; // String轉(zhuǎn)字符串字面量:直接引用地址
不允許let mut (l, r) = (0, 0);,非常抽象。
調(diào)用成員方法的時(shí)候會(huì)自動(dòng)解引用,與是否是函數(shù)參數(shù)無(wú)關(guān)。&會(huì)直接作用于調(diào)用過(guò)方法后的整體。
字符串操作:
let s = String::from("test");
// 遍歷:直接得到字符值
for c in s.chars() {
if c == ' ' {
break;
}
}
s.replace(<要替換的字符串>, <要換成的字符串>);
s.replacen(<要替換的字符串>, <要換成的字符串>, <替換數(shù)目>);
// 只要有一個(gè)String類型,其他的即使是&str也可以相加
不加pub全是隱藏,包括use。
序列要遍歷需要.iter(),但hashmap遍歷不需要,自身就是一個(gè)集合。
遇到任何不需要轉(zhuǎn)移值的地方都要打&。
Some()把一個(gè)變量變成optional變量。
if let和match都是模式匹配的方法。
如果被匹配的option變量在模式匹配后還要使用,那么不能夠使用Some(v),而是要使用Some(ref v),避免所有權(quán)被奪走。
拋出錯(cuò)誤用Err(String),不拋出用Ok(String),返回值都是Result<成功時(shí)返回的類型,錯(cuò)誤時(shí)返回的類型>。
parse自己返回值就是Result類型。
?很厲害,對(duì)于返回Result的,如果Err就立刻返回Err,如果有值就取出值,繼續(xù)執(zhí)行;對(duì)于返回Option的,如果None就立刻返回None,如果有值就解出Some(v)的v,繼續(xù)執(zhí)行。
但使用?的函數(shù)必須是Result返回值,包括main本身。在非錯(cuò)誤情形下,要調(diào)用Ok(())來(lái)返回一個(gè)空單元。
?遇到錯(cuò)誤或None會(huì)立刻返回,但Err并不會(huì),后續(xù)的代碼會(huì)繼續(xù)執(zhí)行。
注意:只有函數(shù)結(jié)束時(shí)的表達(dá)式才能作為返回值。
用Result
泛型寫法:
struct泛型直接在名字后面跟<>impl
trait感覺像C++的虛函數(shù),聲明但不實(shí)現(xiàn)。trait xxx由后面的impl xxx for type來(lái)實(shí)現(xiàn)。
但是trait似乎是任何對(duì)象都可以通用的,作為公共的.xxx()接口。
self是當(dāng)前對(duì)象,Self是當(dāng)前類型。
trait的默認(rèn)函數(shù)跟C++虛函數(shù)一樣,可以在聲明虛函數(shù)處定義默認(rèn)操作。
若只關(guān)心對(duì)象是否有特定trait,只需把對(duì)象類型寫成impl
要是關(guān)心對(duì)象是否有多個(gè)特定trait,只需一個(gè)impl,后面把trait名稱用+連起來(lái)即可。
用#[should_panic(expected = "...")]捕獲panic信息,使得panic不顯示。即“預(yù)料之中的panic”。
unsafe操作:用unsafe <不安全函數(shù)定義>或者unsafe{<不安全運(yùn)算/函數(shù)調(diào)用>},一般把&mut用as轉(zhuǎn)成*mut使用。例程:
unsafe fn modify_by_address(address: usize) {
unsafe {
let ptr = address as *mut u32;
*ptr = 0xAABBCCDD;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_success() {
let mut t: u32 = 0x12345678;
unsafe { modify_by_address(&mut t as *mut u32 as usize) };
assert!(t == 0xAABBCCDD);
}
}
也可以用::into_raw()生成裸指針,然后用::from_raw()解引用。不過(guò)這個(gè)應(yīng)該需要derive?
#[cfg(<邏輯表達(dá)式>)] 實(shí)際上就是條件編譯。
cargo:rustc-env=<環(huán)境變量名>=<值> # 定義環(huán)境變量
cargo:rustc-cfg=<屬性名>{=<值>} # 相當(dāng)于define,值可以省略
extern引入的函數(shù)默認(rèn)是unsafe的,
extern "
...
}
相關(guān)的函數(shù)attribute
#[no_mangle] // 不讓外部函數(shù)覆蓋本地函數(shù)名稱
#[link_name = "xxx"] // 讓這個(gè)外部函數(shù)在本地可以用xxx的名字被調(diào)用
生命周期標(biāo)記:'<字母和數(shù)字的組合,或是下劃線>。一般來(lái)說(shuō)'a、'b這一類就夠用了。
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str // 讓函數(shù)參數(shù)和返回值的生命周期一致
fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'a str // 讓返回值和第一個(gè)參數(shù)的生命周期一致
rust里的花括號(hào)不能亂加,因?yàn)樽饔糜蚝蜕芷趶?qiáng)綁定。
對(duì)于形如
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("long string is long");
let result;
{
let string2 = String::from("xyz");
result = longest(string1.as_str(), string2.as_str());
}
println!("The longest string is '{}'", result);
}
造成的生命周期不統(tǒng)一問(wèn)題只能靠移動(dòng)string2或者println!來(lái)實(shí)現(xiàn),非常抽象。
struct Book<'a> {
++++
author: &'a str,
++
title: &'a str,
++
}
fn main() {
let name = String::from("Jill Smith");
let title = String::from("Fish Flying");
let book = Book { author: &name, title: &title };
// 此后未再用過(guò)name和title,于是name和title的符號(hào)被連同數(shù)據(jù)一起銷毀
// 然而它們的數(shù)據(jù)還會(huì)被book使用,所以要讓author和title的生命周期跟book一樣長(zhǎng)
// 即,author和title借來(lái)的數(shù)據(jù)不會(huì)在name和title的銷毀時(shí)失去
println!("{} by {}", book.title, book.author);
}
一個(gè)典型的vec操作:
fn main() {
let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"];
let mut my_iterable_fav_fruits = my_fav_fruits.iter(); // TODO: Step 1
assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana"));
assert_eq!(my_iterable_fav_fruits.next(), Some(&"custard apple")); // TODO: Step 2
assert_eq!(my_iterable_fav_fruits.next(), Some(&"avocado"));
assert_eq!(my_iterable_fav_fruits.next(), Some(&"peach")); // TODO: Step 3
assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry"));
assert_eq!(my_iterable_fav_fruits.next(), None); // TODO: Step 4
}
需要注意的是,一個(gè).iter()得到的迭代器是空頭的,即,要再next()一下才得到第一個(gè)元素。
沙比String只能+= &str,不能加String。感覺跟shell語(yǔ)言有相似之處,等號(hào)右側(cè)使用的實(shí)際上是$var而不是var。
enum里的結(jié)構(gòu)體要用
注意以下區(qū)別:
for ref x in set.iter() = for x in set.iter()
for mut x in set.iter() // 這種寫法沒(méi)用
for x in set.iter_mut() // 這才是正確的
這里面的x是迭代器,即&
不用for/while,純用迭代器遍歷有以下幾種方法:
let mut vec = Vec::new();
let mut map = HashMap::new();
// for_each,此處寫兩種,以下只寫vec
vec.iter().for_each(|element| {
<執(zhí)行操作>
});
map.iter().for_each(|(key, value)| {
<執(zhí)行操作>
});
vec.iter().map(|element| {
<執(zhí)行操作>
element
}).collect(); // 執(zhí)行體返回element的話相當(dāng)于消耗了vec之后又得到一個(gè)vec
vec.iter().inspect(|&element| {
<執(zhí)行操作>
}).count(); // 執(zhí)行count來(lái)消費(fèi)迭代器
// 對(duì)于字符串是string.chars()而不是iter(),后面差不多
最好用的還是for_each。
thread::spawn(|| {})借用外部變量,thread::spawn(move || {})獲取外部變量所有權(quán)。
spawn返回handle,handle.join()跟蹤線程,并獲取Result形式的返回值。
let a = Arc::new(value); // 創(chuàng)建線程安全的共享變量
let b = Arc::new(Mutex::new(value)); // 創(chuàng)建線程安全的共享互斥變量
let c = a.lock().unwrap(); // 互斥變量只有l(wèi)ock了才能確定,共享變量必須unwrap
for x in xxx.iter()和for x in &xxx是等效的,不過(guò)傳出來(lái)的東西有點(diǎn)區(qū)別。?
定義結(jié)構(gòu)體的時(shí)候可以類型名挨著
幾種智能指針:
let a = <值>;
let shared_a = Arc::new(a); // 生成線程安全變量,并奪權(quán)
let thread_a = Arc::clone(&shared_a); // 對(duì)每個(gè)線程生成單獨(dú)的變量
// 一般來(lái)說(shuō),只有.clone()和傳引用可以不奪權(quán),其余的都會(huì)奪權(quán)。
// Box = unique_ptr in C++
enum List {
Cons(i32, Box),
Nil
} // Box可以允許嵌套定義
let a = Box::new(<值>);
println!("{}", *a); // 相當(dāng)于生成值對(duì)應(yīng)的指針,并且會(huì)自動(dòng)釋放
// Box相當(dāng)于是Rust的常用指針
// Box用于定義或聲明,Box::
::..用于調(diào)用
let a = Cow::new(<變量>); // 直接奪權(quán)
let b = Cow::new(<引用>); // 借用,如果經(jīng)由b想要修改數(shù)據(jù),則奪權(quán)
// Rc = shared_ptr in C++
let a = Rc::new(<變量>); // 創(chuàng)建共享變量
let b = Rc::clone(&a); // 指向同一個(gè)變量
println!("{}", Rc::strong_count(&a)); // 輸出引用計(jì)數(shù)
宏定義:
macro_rules! my_macro {
() => {
println!("Check out my macro!");
}; // 不同分支之間要用分號(hào)隔開
($val:expr) => {
println!("Look at this other macro: {}", $val);
}
}
可以定義在module里,宏上方寫#[macro_export],然后在外面直接用my_macro!()調(diào)用。
Clippy很強(qiáng),多用。
as可以用來(lái)轉(zhuǎn)換類型,也可以用來(lái)改變引進(jìn)的包名。
寫類型轉(zhuǎn)換:
use std::convert::{TryFrom, TryInto};
impl TryFrom<源類型> for 目標(biāo)類型 {
fn try_from(參數(shù): 源類型) -> Result
...
}
}
let a = 目標(biāo)類型::try_from(源類型對(duì)象);
let b: Result<目標(biāo)類型, _> = 源類型對(duì)象.try_into(); // 允許失敗的嘗試類型轉(zhuǎn)換,可以不寫impl
對(duì)于泛型函數(shù),用
From/Into只需要定義其一就夠了,TryFrom/TryInto同理。只是From/Into不會(huì)處理出錯(cuò)情況。
雜亂無(wú)章的知識(shí)學(xué)習(xí)結(jié)束了,接下來(lái)是算法部分。
基礎(chǔ)3 算法實(shí)現(xiàn)
其實(shí)主要是數(shù)據(jù)結(jié)構(gòu),大部分算法所有語(yǔ)言都差不多。
冒泡排序:
fn sort
//TODO
let n = array.len();
for i in 0..n {
for j in i + 1..n {
if array[i] > array[j] {
// 單純的變量交換和數(shù)組元素交換略有不同,前者第三步可以靠重新綁定tmp的值來(lái)
// 完成,不需要克隆兩次,但array[j]不能let重綁定。
let tmp = array[i].clone();
array[i] = array[j].clone();
array[j] = tmp;
}
}
}
}
柚子快報(bào)邀請(qǐng)碼778899分享:Rust學(xué)習(xí)筆記(一)
精彩鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。