精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

24小時快速入門大熱語言 Rust!學不會來找我

開發
最近幾年,Rust在業界的使用越來越多,本篇文章從Rust核心語法等基礎知識入手,進而詳細的介紹Rust語言不同于其它語言的思維方式,最后通過一個實戰的項目,帶大家快速入門Rust編程語言。

作者 | richardyao

最近幾年,Rust在業界的使用越來越多,本篇文章從Rust核心語法等基礎知識入手,進而詳細的介紹Rust語言不同于其它語言的思維方式,最后通過一個實戰的項目,帶大家快速入門Rust編程語言。

最近幾年,Rust在業界的使用越來越多。在Windows內核中(win32kbase_rs.sys)、Linux內核中、Chromium瀏覽器中都有Rust的身影,AWS S3也使用rust重構了他們的核心存儲服務ShardStore,Azure的CTO甚至說"Speaking of languages, it's time to halt starting any new projects in C/C++ and use Rust for those scenarios where a non-GC language is required."。從下面的Google Trends也可以看的出來,Rust的熱度正在上升,并且增長很快,可以說現在是學習Rust最好的時機。

本篇文章從Rust核心語法等基礎知識入手,進而詳細的介紹Rust語言不同于其它語言的思維方式,最后通過一個實戰的項目,帶大家快速入門Rust編程語言。

有C++、Golang、Python這些語言基礎的話,大部分知識都可以遷移過去,再加上大模型的輔助,24小時快速入門,是有可能達成的。

一、基礎篇

1. Rust的安裝與基本工具的使用

Rust的安裝直接參考官網的文檔,這里不做更具體的介紹了:https://www.rust-lang.org/tools/install。

在國內使用Rust的話,可以通過 https://rsproxy.cn/ 網站提供的鏡像,更方便快捷的安裝Rust以及下載相關的crate等。

Rust對應的編譯器是rustc,但是這個大家平時使用的并不多,更多的是通過包管理工具cargo等來管理、構建項目。cargo常用命令如下:

cargo build
cargo build --release
cargo clippy
cargo run 
cargo run -- --help
cargo clean
cargo check
cargo doc
cargo expand      # 需要使用cargo install cargo-expand先安裝

Rust日常開發可以使用vscode + rust-analyzer插件。

2. 通過greplite小程序熟悉Rust的語言特點

在這里,我們使用一個簡單greplite程序來介紹Rust語言的特點。

先使用cargo new greplite命令創建一個binary的crate,然后在main.rs中輸入下面的代碼:

use std::env;
use std::fs::File;
use std::io::{self, BufRead, BufReader};

fn main() -> io::Result<()> {
    let args = env::args().collect::<Vec<_>>();
    if args.len() < 3 {
        eprintln!("Usage: greplite <search_string> <file_path>");
        std::process::exit(1);
    }

    let search_string = &args[1];
    let file_path = &args[2];

    run(search_string, file_path)
}

fn run(search_string: &str, file_path: &str) -> io::Result<()> {
    let file = File::open(file_path)?;
    let reader = BufReader::new(file);

    for line in reader.lines() {
        let line = line?;
        if line.contains(search_string) {
            println!("{line}");
        }
    }

    Ok(())
}

這個程序的第一個參數是要搜索的字符串,第二個參數是搜索的文件,比如說要搜索src/main.rs文件中包含main函數的行,可以如下執行:

$ cargo run -- main src/main.rs
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/greplite main src/main.rs`
fn main() -> io::Result<()> {

這個小程序一共就30行代碼,但是知識點還挺多的,先整體介紹一下:

① use類似于C++中的include,用來引用std標準庫里面的module;

② fn用來定義一個函數,main函數是整個程序的入口;

③ ->表示函數的返回值,io::Result<()>表示返回的是個Result,通過文檔,或者vscode代碼跳轉的方式,可以看到std::io::Result<T>是基于std::result::Result<T, std::io::Error>定義的一個新類型;

https://doc.rust-lang.org/std/io/type.Result.html

pub type Result<T> = std::result::Result<T, std::io::Error>;

同時std::result::Result是一個枚舉:

pub enum Result<T, E> {
    Ok(T),
    Err(E),
}

Result表示的值,可以是表示成功的Ok,也可以是表示失敗的Err。

上面pub type Result<T> = std::result::Result<T, std::io::Error>;只是把std::io::Result的錯誤類型固定成std::io::Error,表示只會返回std::io::Error類型的錯誤。

④ io::Result<()>中的(),表示的是一個tuple,這個tuple沒有任何元素,也稱為unit;

⑤ 關于main函數的返回類型:https://doc.rust-lang.org/reference/crates-and-source-files.html?highlight=main#main-functions;

⑥ let args = env::args().collect::<Vec<_>>();中的env::args()返回的值,實現了Iterator這個trait,關于Rust中的迭代器,后面還會重點來講,這個collect是把迭代器的列表收集起來,構造成一個Vec。

這個語句還有下面幾種寫法,都是可以的:

let args = env::args().collect::<Vec<_>>();
let args = env::args().collect::<Vec<String>>();
let args: Vec<String> = env::args().collect();
let args: Vec<_> = env::args().collect();

Rust的類型推斷是非常強大的,如果后面對args的使用能確定args的類型的話,也可以完全寫成:

let args = env::args().collect();

比如說如果有函數fn print_args(args: &Vec<String>) {},同時后面調用了print_args(&args)的話,args的定義就完全不需要類型注解了。

另外,通過let定義的變量,默認是不可變的(immutable),如果要修改的話,需要顯示的使用let mut args = env::args().collect();

這里還有一個知識點,我們在前面并沒有通過use語句引入Vec,那為什么不報錯呢?

https://doc.rust-lang.org/std/prelude/index.html

Rust編譯器預先已經包含了std中部分常用的組件,這樣代碼會更簡潔一些。

⑦ args.len()中的len()是Vec<String>的成員函數 https://doc.rust-lang.org/std/vec/struct.Vec.html#method.len;

⑧ let search_string = &args[1];和let file_path = &args[2];這里定義了兩個引用,是對Vec<String>中對應元素的不可變的借用;

args[1]下標操作,在運行時會check下標是否越界,但是由于前面判斷了長度,因此這里的運行時的越界檢查,編譯器通常會優化掉。

⑨ 下面是調用run函數,另外這里是一個expression,expression的值作為整個main函數的返回值;

j)、第19行let file = File::open(file_path)?;打開一個文件,這個語句中的?是一個語法糖,這條語句等價于:

let file = match File::open(file_path) {
        Ok(f) => f,
        Err(err) => return Err(From::from(err)),
    };

https://doc.rust-lang.org/reference/expressions/operator-expr.html?highlight=question#the-question-mark-operator

⑩ 第20行let reader = BufReader::new(file);file實現了Read這個trait,然后BufReader是在Read這個trait的基礎上,做了一層封裝,實現了帶緩存的讀,并在此基礎上,提供了lines()等便捷的方法。

https://doc.rust-lang.org/std/fs/struct.File.html#impl-Read-for-File

BufReader實現了BufRead這個trait,因此在use std::io::BufRead;之后,可以調用這個trait對應的lines()等方法。

https://doc.rust-lang.org/std/io/struct.BufReader.html#impl-BufRead-for-BufReader

? 第22行for line in reader.lines() {lines()函數返回了一個迭代器,然后for line in的方式來遍歷這個迭代器,這個迭代器對應的Item為

type Item = Result<String, Error>

https://doc.rust-lang.org/std/io/struct.Lines.html#associatedtype.Item

這個被稱為trait的Associated type。

? 這個迭代器的Item是一個Result,因此第23行使用?運算符把其轉換成了普通的String;

? 第24行if line.contains(search_string) {判斷line是否包含要搜索的子串;

? 第25行println!("{line}");打印輸出,和println!("{}", line);等價,前面這種方式被稱為named parameters:

https://doc.rust-lang.org/std/fmt/index.html#named-parameters

另外println!包括前面的eprintln!最后的這個!,表示這是一個宏。

Rust中,函數不支持可變參數,通過宏的方式來實現可變參數。

? 最后第29行,Ok(())這個expression作為整個函數的返回值表示成功。

? 同時我們注意到search_string和file_path都是&String類型的,run函數的參數&str是什么鬼?

類比于C++中的string和string_view,同時string到string_view可以通過string的operator string_view進行隱式轉換:

https://en.cppreference.com/w/cpp/string/basic_string/operator_basic_string_view.html

在rust中,String到&str也可以進行隱式轉換:

思考題:

① 在上面的30行代碼中,一共涉及到哪些trait?

Read、BufRead、Iterator、FromIterator、From、Deref、Termination、Drop

② 在上面的30行代碼中,一共有哪些迭代器iterator?

std::env::Args、std::io::Lines

從上面的這個小例子中,我們也能一窺Rust程序的特點:

  • 代碼風格,下劃線小寫命名的形式;
  • 傾向于使用trait,使用組合的方式來實現程序的功能;
  • 迭代器iterator功能挺強大的;
  • Rust學習曲線確實很陡峭,30行代碼竟然涉及這么多語法。

3. Rust中組織數據的3種方式

在Rust中,我們可以使用struct來組織數據。

struct Person {
    first_name: String,
    last_name: String,
    age: i32,
}

也可以使用tuple(Rust中的tuple和python中的tuple概念是一致的):

let person_info = ("Harry", "Potter", 18);
let first_name = person_info.0;
let (first_name, _, _) = person_info;

Rust中的enum表示的可以是一個集合類型中的任意一種:

enum WebEvent {
    // An enum variant without any data.
    PageLoad,
    // An enum variant with a string slice.
    KeyPress(char),
    // An enum variant with a struct.
    Click { x: i32, y: i32 },
    // An enum variant with an owned String.
    Paste(String),
}

impl WebEvent {
    fn describe(&self) {
        matchself {
            WebEvent::PageLoad => println!("Page loaded"),
            WebEvent::KeyPress(c) => println!("Key pressed: {}", c),
            WebEvent::Click { x, y } => println!("Clicked at: ({}, {})", x, y),
            WebEvent::Paste(s) => println!("Pasted: {}", s),
        }
    }
}

enum通常配合match在一起使用。

4. Rust中的Ownership

Rust中的ownership規則:

1. Each value in Rust has an owner.
2. There can only be one owner at a time.
3. When the owner goes out of scope, the value will be dropped.

通過ownership的機制實現RAII,當變量離開作用域的時候,會被釋放或者drop掉。

Rust在默認的情況下,是move語義的,比如說:

let a = vec![1, 2, 3];
let b = a;
println!("{:?}", a);
println!("{:?}", b);   // error[E0382]: borrow of moved value: `a`

但是如果對應的類型實現了Copy這個trait的話,默認就會走copy的語義:

let a = 1;
let b = a;
println!("{:?}", a);
println!("{:?}", b);

https://doc.rust-lang.org/std/marker/trait.Copy.html

Rust中為很多簡單類型都自動實現了Copy這個trait。

Copy和Clone的區別:

#[derive(Copy, Clone)]
struct Point {
   x: i32,
   y: i32,
}

在上面Point的定義中,由于i32同時實現了Copy和Clone這兩個trait,因此他們組合在一起,Point也能實現這兩個Trait。

#[derive(Clone)]
struct PointList {
    points: Vec<Point>,
}

而PointList中,由于Vec只實現了Clone這兩個trait,因此PointList也只能實現Clone這個trait,不能實現Copy。

Copy和Clone的區別,就類似于淺拷貝和深拷貝的區別,上面Point的定義,兩個變量都是分配在棧上的,淺拷貝和深拷貝沒有區別,因此Point可以同時實現Copy和Clone這兩個trait;而下面PointList的定義中,Vec在棧上只記錄了元信息(pointer, capacity, length),Vec的元素是存放在堆上的,只能深拷貝,因此只實現了Clone這個trait。

5. Rust中的引用和借用

1. At any given time, you can have either one mutable reference or any number of immutable references.
2. References must always be valid.

由于Rust中默認是move語義的,在有的場景下,我并不想轉移ownership,這種情況下,可以通過引用來借用。

引用分為兩種,一種是immutable引用,一種是mutable的引用。

通過immutable的引用,借用者不能修改;通過mutable的引用,借用者可以對這個變量做任何的修改,比如說賦值、swap等,唯一的一個限制就是要保證這個變量的完整性。

Rust的安全機制要求引用在任何時候都必須有有效;同時,限制mutable引用和immutable引用不能同時存在:你可以有多個immutable的引用;也可以有一個mutable的引用;但是不允許有多個mutable的引用,也不允許mutable的引用和immutable的引用同時存在。

思考:為什么對同一個變量的mutable引用和immutable引用不能同時存在?思考下面的例子:

fn main() {
    let mut v = vec![1, 2, 3, 4];
    let first = &v[0];
    v.push(5);   // error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
    println!("first element: {}", first);
}

6. trait

Rust中的trait類似于golang中的interface,Rust中通過trait定義共同的行為。比如說咱們定義Shape形狀這樣的trait,所有的形狀都有面積:

trait Shape {
    fn area(&self) -> f64;
}

圓和長方形都能實現Shape形狀這個Trait:

struct Circle {
    radius: f64,
}

impl Shape for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * self.radius * self.radius
    }
}

struct Rectangle {
    width: f64,
    height: f64,
}

impl Shape for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

有了這些定義之后,就可以實現編譯時的類型約束:

fn print_area1<S: Shape>(shape: &S) {
    println!("The area is: {}", shape.area());
}

fn print_area2(shape: &impl Shape) {
    println!("The area is: {}", shape.area());
}

上面這兩種實現的方式是等價的,impl只是一個語法糖:https://doc.rust-lang.org/stable/reference/types/impl-trait.html

也可以實現運行時的多態:

fn print_area3(shapes: &[&dyn Shape]) {
    for shape in shapes {
        println!("The area is: {}", shape.area());
    }
}

調用上面幾個方法的例子:

fn main() {
    let circle = Circle { radius: 5.0 };
    let rectangle = Rectangle {
        width: 10.0,
        height: 4.0,
    };

    print_area1(&circle);
    print_area2(&rectangle);

    let shapes: Vec<&dyn Shape> = vec![&circle, &rectangle];
    print_area3(&shapes);
}

7. 迭代器

Rust中的迭代器由Iterator這個trait來表示,表示會產生一個序列的值:https://doc.rust-lang.org/std/iter/trait.Iterator.html

這個trait只有一個Required的方法next,當next返回None的時候,表示序列結束:

// Required method
fn next(&mut self) -> Option<Self::Item>;

可以直接調用next方法來一個一個的獲取值,但是更多的場景下是使用標準庫中提供的adapter和consume方法。

rust中的迭代器有以下特點:

① laziness

https://doc.rust-lang.org/std/iter/index.html#laziness

② infinity

https://doc.rust-lang.org/std/iter/index.html#infinity

③ 高效,性能很高,和手寫for循環性能是一致的。

struct Fibonacci {
    current: u64,
    next: u64,
}

impl Fibonacci {
    fn new() -> Fibonacci {
        Fibonacci { current: 0, next: 1 }
    }
}

implIteratorfor Fibonacci {
    type Item = u64;

    fn next(&mutself) -> Option<Self::Item> {
        let next_number = self.current + self.next;
        self.current = self.next;
        self.next = next_number;
        Some(self.current)
    }
}

fn main() {
    let fib_iterator = Fibonacci::new();

    println!("The first 10 Fibonacci numbers are:");
    for number in fib_iterator.take(10) {
        println!("{}", number);
    }

    let fib_vec: Vec<u64> = Fibonacci::new().take(15).collect();
    println!("\nThe first 15 numbers collected into a vector:");
    println!("{:?}", fib_vec);
}

8. 閉包

Rust中的閉包能夠capture環境中的值,根據capture的方式不同,閉包也分別實現了不同的trait,如果是普通的borrow來capture的話,實現了Fn,如果是mut borrow來capture的話,實現了FnMut,如果是move consume了變量的話,實現FnOnce,看下面的例子:

fn main() {
    let s = String::from("hello");
    let f1 = || &s;
    println!("{}", f1());
    println!("{}", f1());
}

如上面的代碼f1是一個閉包,capture了變量s的引用,編譯器自動幫這個閉包實現了Fn的trait,這個閉包可以調用多次。我們也可以看到rust標準庫中Fn這個trait的定義是 fn call(&self, args: Args) -> Self::Output;,傳遞的是self的引用,因此才可以調用多次。

fn main() {
    let mut s = String::from("hello");
    let mut f2 = || s += "world";
    f2();
    //println!("{}", s);
    f2();
    println!("{}", s);
}

如上面的代碼,f2也是一個閉包,mut borrow了s,因此編譯器自動幫這個閉包實現了FnMut這個trait,注意,上面的代碼中,如果注釋掉中間的println!的話,會報error[E0502]: cannot borrow s as immutable because it is also borrowed as mutable錯誤,f2是變量s的一個mut引用,要滿足s引用的限制規則。FnMut在標準庫中是這樣定義的:fn call_mut(&mut self, args: Args) -> Self::Output;,可以看到第一個參數是&mut self。

fn main() {
    let s = String::from("hello");
    let f3 = || s;
    println!("{}", f3());
    //println!("{}", f3());  // error[E0382]: use of moved value: `f3`
}

上面的代碼中f3成為了s的owner,實現了FnOnce這個trait,f3只能調用一次,第二次調用的話會報use of moved value的錯誤信息。FnOnce在標準庫中的定義:fn call_once(self, args: Args) -> Self::Output;,self是move的這種調用方式,因此只能調用一次。

同時,編譯器為實現了Fn的閉包,也同時實現了FnMut和FnOnce;實現了FnMut的閉包也同時實現了FnOnce。

9. Sync & Send

Rust中的并發安全,是通過Sync和Send這兩個trait來體現的。Send表示的含義是,變量可以跨越線程的邊界進行傳遞;Sync表示的含義是,變量可以多線程同時訪問。

這里通過一個簡單的例子,演示下Sync & Send如何保證并發安全的:

use std::sync::Arc;
use std::sync::Mutex;
use std::thread;

fn test1() {
    letmut a = vec![1, 2, 3];

    let handler = std::thread::spawn(move || {
        a.push(4);
    });

    handler.join().unwrap();
}

fn test2() {
    letmut a = vec![1, 2, 3];

    thread::scope(|s| {
        s.spawn(|| {
            println!("hello from the first scoped thread");

            a.push(4);
        });
    });

    a.push(5);
}

fn test3() {
    let a1 = Arc::new(vec![1, 2, 3]);
    let a2 = a1.clone();

    let handler = thread::spawn(move || {
        println!("a1 {:?}", a1);
    });

    println!("a2 {:?}", a2);

    handler.join().unwrap();
}

fn test4() {
    let a1 = Arc::new(Mutex::new(vec![1, 2, 3]));
    let a2 = a1.clone();
    let a3 = a1.clone();

    let handler1 = thread::spawn(move || {
        letmut lock_guard = a1.lock().unwrap();
        lock_guard.push(4);
    });

    let handler2 = thread::spawn(move || {
        letmut lock_guard = a2.lock().unwrap();
        lock_guard.push(4);
    });

    handler1.join().unwrap();
    handler2.join().unwrap();
    println!("a3 {:?}", a3.lock().unwrap());
}

fn main() {
    test1();
    test2();
    test3();
    test4();
}

10. async & await

Rust的異步編程,被稱為無棧協程,先看一個簡單的例子:

use anyhow::Result;
use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct Joke {
    joke: String,
}

#[tokio::main]
asyncfn main() -> Result<()> {
    let res = reqwest::get("https://geek-jokes.sameerkumar.website/api?format=json").await?;
    let joke = res.json::<Joke>().await?;
    println!("{}", joke.joke);
    Ok(())
}

使用cargo expand命令,上面的代碼,main大概展開成下面的樣子:

fn main() -> Result<()> {
    tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .expect("Failed building the Runtime")
        .block_on(async {
            let res = reqwest::get("https://geek-jokes.sameerkumar.website/api?format=json").await?;
            let joke = res.json::<Joke>().await?;
            println!("{}", joke.joke);
            Ok(())
        })
}

從上面的代碼可以看到,代碼中首先構建了一個tokio的runtime,然后block_on在某個async塊上進行執行。

async/await把Rust程序分割成了兩個世界,在async/await的上下文里,不能調用阻塞的函數,不然會卡住異步運行時tokio的執行和調度。

為了弄清楚async/await到底干了啥,咱們首先看下上面代碼中的其中一行代碼:

let res = reqwest::get("https://geek-jokes.sameerkumar.website/api?format=json").await?;

這行代碼可以拆成兩行:

let res_future = reqwest::get("https://geek-jokes.sameerkumar.website/api?format=json");
let res = res_future.await?;

在vscode里面,把鼠標懸停在res_future上,可以看到vscode給出的類型注解是:

let res_future: impl Future<Output = Result<Response, Error>>

可以看到res_future實現了Future這個trait,但是res_future具體的類型不知道,只知道他實現了Future這個trait。

async只是一個語法糖:

async fn test() {
    println!("This is a test function.");
}

fn test2() -> impl Future<Output = ()> {
    async {
        println!("This is a test function.");
    }
}

async的本質,實際上是編譯器把reqwest::get("https://geek-jokes.sameerkumar.website/api?format=json")編譯成了一個狀態機,然后這個狀態機實現了Future這個trait,所以這里get返回的時候,實際上并沒有發出任何http請求,只是返回了一個狀態機,這個狀態機實現了Future這個trait,僅此而已。理論上來說,也可以手寫一個struct,實現同樣的狀態機,只是這個過程會特別的復雜,編譯器直接幫忙咱們做了:https://doc.rust-lang.org/stable/reference/items/functions.html#r-items.fn.async

一些手動實現Future的例子:

await的本質,實際上是“不停的”調用上面狀態機的poll方法,驅動狀態機不停的往前走,直到Ready為止。

https://doc.rust-lang.org/stable/reference/expressions/await-expr.html?highlight=await#r-expr.await.effects

rust異步編程的核心,就是Future這個trait:

pub trait Future {
    type Output;

    // Required method
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

pub enum Poll<T> {
    Ready(T),
    Pending,
}

上面Future這個trait的定義中,poll函數的第一個參數是個Pin<&mut Self>的類型,什么是Pin,為什么需要Pin呢?

還是要從上面async生成的狀態機說起,reqwest::get("https://geek-jokes.sameerkumar.website/api?format=json")這個函數會返回一個對象,這個對象實現了Future trait,這個對象是一個狀態機,內部維護這個請求的執行狀態,然后await的時候,會“不停”的poll,驅動狀態機往前走,這個狀態機內部會維護很多的狀態,比如說tcp socket收發的buffer,以及buffer已經使用的大小等。換句話說,這個狀態機是一個自引用的對象,狀態機內部有一些buffer,然后狀態機內部有一些指針指向這些buffer的某些位置等。狀態機是自引用的,就要求這個狀態機不能在內存中隨意的移動,如果移動的話,自引用指針的指向就錯了,指向了別的位置。Rust對這個問題的解法就是加一層Pin,對這個狀態機的所有的訪問,都是通過Pin這個智能指針來訪問的,Pin限制了這個狀態機不能移動和復制。

異步任務的取消:不.await了,不poll了,異步的請求也就取消了,比如說前面提到的 https://docs.rs/futures-util/latest/futures_util/future/struct.Select.html 當其中的一個已經Ready之后,另外一個就自動的取消了。

二、思維篇

每個語言都有自己的特點,比如說Golang推崇通過消息的方式共享內存,Python語言中的list comprehensions是一種強大且簡潔的創建列表的方法等,這里介紹下Rust語言的思維方式。

1. expression

在Rust中,推崇簡潔,表達式可以作為值進行賦值,或者作為返回值:

fn foo(x: usize) -> usize {
    x
}

表達式可以賦值:

let x = if 3 > 2 {
 1
} else {
 2
};

前面的greplite的main函數,也可以寫成:

fn main() -> io::Result<()> {
    let mut args = env::args().skip(1);

    let (search_string, file_path) = match (args.next(), args.next(), args.next()) {
        (Some(s), Some(f), None) => (s, f),
        _ => {
            eprintln!("Usage: greplite <search_string> <file_path>");
            std::process::exit(1)
        }
    };

    run(&search_string, &file_path)
}

2. split

由于Rust中ownership及引用規則的限制,有些對象會有split的操作,比如說Vec,split成兩個,每個只修改Vec的一部分元素,這樣整體還是安全的。

再比如說socket,可以split成一個只收數據的socket,一個只發數據的socket,這樣可以實現在一個線程中只收數據,在一個線程中只發數據。

https://docs.rs/tokio/latest/tokio/net/struct.TcpStream.html#method.into_split

3. 無處不在的Option和Result

就像在golang中,if err != nil {}和if someData != nil {}無處不在一樣,在Rust中,Option和Result也是隨處可見;Option和Result都是enum,如果都用match來進行判斷的話,代碼的遞進深度會比較深,另外代碼也看起來會很冗余。

為此Rust為Option和Result提供了很多便利的操作。

① question mark operator

https://doc.rust-lang.org/reference/expressions/operator-expr.html#r-expr.try

② is_some()、is_none()、is_ok()、is_err()便捷函數

③ let else賦值操作

let a = Some(1);
let Some(b) = a else {
  return;
};

④ 和迭代器的互操作等

熟練掌握上面的方法,會讓代碼更簡潔。

4. match的不僅僅是enum

在Rust中,match通常用來作用在enum上,然后每個分支判斷enum的每個變體。

但是match不僅僅可以用在enum上,在其它的場景中,match也能發揮大作用。

另外,match是exhaustive的,需要寫出所有的可能的分支。

5. 宏強大的超乎想象

宏的3種場景:

① 類似于println!

本質是編譯器一些規則的替換

https://doc.rust-lang.org/stable/reference/macros-by-example.html?highlight=hygiene#hygiene

② 類似于前面例子中的#[tokio::main]

本質是在編譯期,把對應注解的函數的Token Stream給到這個宏,然后這個宏,在編譯期生成新的代碼。

一個簡單這種宏的例子,可以參考下面的這篇文章:

樂學Rust:100行代碼實現簡易集成測試框架

③ 類似于Debug宏

https://doc.rust-lang.org/std/fmt/derive.Debug.html

本質是在編譯期,把對應注解的對應的struct的Token Stream給到這個宏,然后這個宏,在編譯期生成新的代碼。

6. 通過傳遞消息的方式共享內存

在Golang中,推崇通過消息的方式共享內存。同樣,在Rust中,也支持這種編程的模式。

考慮對象從一個存儲桶搬遷到另外的存儲桶這種場景:

一種操作流程的組織形式可能類似于上圖,左邊的routine,調用list objects的接口,或者從文件列表中獲取所有的對象列表,然后再把這些對象,放入到一個channel中;然后右邊的copy的routine,具體執行copy每個對象的動作。

list操作和具體的copy操作在流程上做了分離,代碼簡潔清晰。

上面這個流程有一個小問題就是,當遇到大對象的時候,大對象可能會成為長尾的瓶頸,因為每個對象都是單routine拷貝的。流程上可以優化如下:

如上圖,再加一層,真正的copy操作只在worker中進行處理,在copy這一層,把每個對象的copy任務,拆分成多個task,如果對象比較小的話,對應一個task,對象比較大的話,采用分塊上傳的方式,拆分成多個task。上圖中的第二個channel里面的消息就是每個task任務,同時每個task任務中會包含另外一個channel,這個worker通過這個channel告知copy的routine對應的task的完成情況。

通知這種流程的組織形式,解決了大對象長尾的問題。流程依然清晰簡潔。

在Rust中同樣可以實現上面的這種流程模式。

不過對比Rust和Golang中channel和select的體驗,由于Rust不是像golang那樣,在語言本身支持channel和select,因此體感上,Rust稍微差了一丟丟。

7. 和C++一致的內存模型

Rust采用和C++一致的內存模型,都是通過atomic原子操作來體現的,C++上的經驗可以直接遷移到Rust上。

比如說,考慮配置熱加載的場景,一種可能的實現是這樣的,一個atomic的pointer指向當前的配置,然后有一個線程從本地或者通過sdk周期性從外部取最新的配置,然后再atomic的更新pointer指向最新的配置。

這里有個問題是,原來的配置何時釋放的問題(safe memory reclamation),在C++中,通常使用hazard pointer來解決,在Rust中也類似,也有hazard pointer。

不過最新的這種問題的解決方式,建議使用《Concurrent Deferred Reference Counting with Constant-Time Overhead》這篇paper介紹的方法,使用更方便:https://github.com/cmuparlay/concurrent_deferred_rc

對應Rust的crate:https://github.com/aarc-rs/aarc

關于Rust內存模型的書籍推薦。

https://marabos.nl/atomics/

8. Interior Mutability Pattern

由于Rust的ownership以及引用規則的限制,在寫代碼的時候,要想好各種數據結構,是否會跨多線程訪問,如果跨多線程訪問的話,可能要使用interior mutability pattern,所有的struct的函數都是&self,而不是&mut self。

參考例子:https://github.com/yaozongyou/rust-24-hour-crash-course/blob/bdf5bc30a67fe6b5649a8fff7cc25e2e0d19a0e6/mini-redis/src/store.rs#L45

9. build.rs在編譯期執行各種操作

crate有個build.rs腳本,可以獲取代碼倉庫的git信息,編譯c/c++程序等:https://doc.rust-lang.org/cargo/reference/build-scripts.html

https://github.com/yaozongyou/rust-24-hour-crash-course/blob/bdf5bc30a67fe6b5649a8fff7cc25e2e0d19a0e6/mini-redis/build.rs

10. 迭代器真的很好用

在Rust中,適當的使用迭代器會讓代碼更簡潔。各種collection(Vec、HashMap、BTreeMap等)都能通過迭代器來遍歷,Result和Option等也都能和迭代器相互轉換等,迭代器也有特別多的adapter。

從C++轉到Rust的話,可以嘗試多使用下迭代器。

https://doc.rust-lang.org/std/iter/

之前greplite程序,可以改下成:

fn run(search_string: &str, file_path: &str) -> io::Result<()> {
    let file = File::open(file_path)?;
    let reader = BufReader::new(file);

    reader.lines().try_for_each(|line| {
        let line = line?;
        if line.contains(search_string) {
            println!("{}", line);
        }
        Ok::<(), _>(())
    })
}

或者:

fn run(search_string: &str, file_path: &str) -> io::Result<()> {
    let file = File::open(file_path)?;
    let reader = BufReader::new(file);

    reader
        .lines()
        .collect::<io::Result<Vec<_>>>()?
        .into_iter()
        .filter(|line| line.contains(search_string))
        .for_each(|line| println!("{}", line));

    Ok(())
}

11. 想定義個全局變量真不容易

關于全局變量,下面這篇文章總結的非常好:

https://www.sitepoint.com/rust-global-variables/

另外,上面這篇文章寫的比較久了,上圖中的lazy_static or once_cell,在當前最新的Rust的版本中,可以使用OnceLock 或者 LazyLock來替代,這樣就不需要依賴第三方的crate了。

三、實戰篇

使用Rust實現一個mini-redis:https://github.com/yaozongyou/rust-24-hour-crash-course/tree/bdf5bc30a67fe6b5649a8fff7cc25e2e0d19a0e6/mini-redis

Rust學習建議:

rustlings上面的練習全部走一遍。

  • 不要嘗試寫鏈表、不要嘗試寫鏈表、不要嘗試寫鏈表。

參考資料:

責任編輯:趙寧寧 來源: 騰訊技術工程
相關推薦

2022-02-22 08:25:51

typeScript泛型概念泛型使用

2021-02-16 00:33:56

Python語言工具

2021-12-21 10:26:39

交付項目Jira開發

2020-10-09 09:49:18

HTTPS網絡 HTTP

2012-05-16 09:53:56

2019-08-27 10:55:59

MySQLJOINSQLite

2020-03-13 14:20:02

代碼開發 Rust

2020-12-28 10:31:38

服務中斷網絡攻擊網絡安全

2019-05-30 15:20:04

webpack前端開發

2024-04-11 13:13:27

2021-05-10 08:34:37

USB接口USB網絡設備

2015-01-06 17:06:01

創新App

2012-03-22 15:15:58

微軟

2022-01-23 16:13:06

比特幣加密貨幣暴跌

2011-05-16 09:22:28

SQL語言

2011-05-16 09:15:22

SQL語言

2018-01-29 11:11:35

Linux入門學習

2025-01-24 15:09:43

2012-05-23 14:38:37

微軟戴爾

2014-07-01 10:05:15

遠程銀行華為
點贊
收藏

51CTO技術棧公眾號

www黄色在线| 国产麻豆日韩| 91嫩草|国产丨精品入口| 国产不卡精品| 亚洲国产人成综合网站| 久久99久久精品国产| www.久久久久久久| 亚洲国产日韩欧美在线| 亚洲韩国青草视频| 一区二区三区免费播放| 1024在线播放| 久久综合九色综合欧美98| 国产日韩欧美一二三区| 久久久久亚洲av无码专区| 奇米狠狠一区二区三区| 日韩欧美亚洲国产另类| 激情婷婷综合网| 香蕉成人app免费看片| 久久久久高清精品| 91视频在线免费观看| 国产嫩bbwbbw高潮| 欧美啪啪一区| 中文字幕精品一区久久久久| 中文字幕18页| 狂野欧美性猛交xxxx| 偷拍一区二区三区| 国产盗摄视频在线观看| 精品亚洲成a人片在线观看| 国产一区二区三区久久悠悠色av| 欧美在线观看网站| www青青草原| 日韩精品网站| 亚洲精品日韩在线| 欧美一级片在线免费观看| 国产综合色区在线观看| 亚洲高清免费观看| 99久re热视频精品98| 国产女人在线视频| av成人免费在线观看| 91中文精品字幕在线视频| 无码人妻丰满熟妇区bbbbxxxx| 国产精品jizz在线观看美国| 波霸ol色综合久久| 亚洲av毛片基地| 伊人久久大香线蕉无限次| 亚洲精品在线一区二区| 伊人精品视频在线观看| 在线高清欧美| 6080亚洲精品一区二区| 亚洲精品www.| 国产精品亲子伦av一区二区三区| 色香蕉成人二区免费| 男人揉女人奶房视频60分| 第一中文字幕在线| 亚洲成人综合网站| 国产综合av在线| 嗯~啊~轻一点视频日本在线观看| 伊人性伊人情综合网| 日本丰满大乳奶| 成人高清免费在线| 亚洲品质自拍视频| 激情五月六月婷婷| 黑人玩欧美人三根一起进| 亚洲一区二区三区视频在线播放| 大片在线观看网站免费收看| 最新超碰在线| 亚洲国产乱码最新视频| 国产青草视频在线观看| av电影在线免费| 欧美日韩国产黄| 国产精品丝袜久久久久久消防器材| 国产盗摄——sm在线视频| 欧美日韩视频免费播放| 久久人妻精品白浆国产| 在线成人视屏| 欧美另类一区二区三区| 91网址在线观看精品| 亚洲成人五区| 日韩成人激情视频| 无码人妻aⅴ一区二区三区69岛| 日韩av在线中文字幕| 久久激情五月丁香伊人| 精品国产乱码久久久久久鸭王1 | 日韩三级电影| 日本www在线| 亚洲一区二区三区自拍| 97xxxxx| 国产精品久久乐| 日韩一区二区免费在线观看| 在线免费观看污视频| 国产中文精品久高清在线不| 久久天天躁狠狠躁夜夜av| 国产在线综合网| 三级影片在线观看欧美日韩一区二区| 国产精品一区二区三区久久| 亚洲欧美激情国产综合久久久| 91麻豆蜜桃一区二区三区| 天堂资源在线亚洲资源| 污视频免费在线观看| 狠狠躁18三区二区一区| 手机免费看av网站| 亲子伦视频一区二区三区| 中文字幕日韩欧美在线| 玖玖爱免费视频| 日本亚洲三级在线| 大波视频国产精品久久| 岛国在线大片| 午夜成人免费电影| 国内自拍第二页| 亚洲精品国产精品粉嫩| 色综合天天综合网国产成人网| 免费看毛片网站| 成人性生交大片| 亚洲欧美精品在线观看| 黄色aa久久| 欧美精品日日鲁夜夜添| 成年人网站免费看| 黄页网站一区| 91亚洲va在线va天堂va国| 爽爽视频在线观看| 亚洲一级二级三级在线免费观看| 一个色综合久久| 国内精品视频在线观看| 97久久精品国产| www.午夜激情| 自拍av一区二区三区| 女人另类性混交zo| 日韩精品欧美大片| 欧美黑人性生活视频| 亚洲一级特黄毛片| 欧美精彩视频一区二区三区| 男女视频网站在线观看| 亚洲专区**| 欧美超级乱淫片喷水| 中文字幕在线观看高清| 国产亚洲va综合人人澡精品 | 日本aa在线| 欧美男生操女生| 四虎成人免费影院| 日韩专区欧美专区| 久久久久久99| 欧美裸体视频| 日韩成人中文字幕| 久久久久性色av无码一区二区| 国产一区二区三区四区五区入口 | 精品无码人妻一区二区免费蜜桃| 欧美午夜不卡影院在线观看完整版免费| 国产一区在线播放| 免费网站成人| 欧美精品久久久久久久久老牛影院 | 久久久精品免费看| 99久久99久久精品免费观看| 国产精品成人久久电影| 国产96在线亚洲| 久久人人看视频| 日韩一区免费视频| 懂色av一区二区三区| 亚洲色图14p| 久久国产精品亚洲77777| 热舞福利精品大尺度视频| 欧美电影免费观看网站| 亚洲欧美激情另类校园| 中文字幕在线天堂| 中文字幕不卡在线播放| 美女网站色免费| 久久久国产精品| 91免费看网站| 最新欧美色图| 在线播放国产精品| 国产尤物视频在线观看| 亚洲柠檬福利资源导航| 少妇熟女视频一区二区三区| 国产精品毛片一区二区三区| 日本一区二区精品| 欧美大陆国产| 欧美极品在线视频| 免费在线超碰| 911精品产国品一二三产区| 欧美人与禽zozzo禽性配| 99精品国产热久久91蜜凸| 成熟老妇女视频| 91精品国产视频| 国产一区二区三区四区五区在线 | 成人女保姆的销魂服务| 欧美videosex性极品hd| 日韩精品免费综合视频在线播放 | 破处女黄色一级片| 不卡视频在线看| 国产又黄又猛又粗又爽的视频| 亚洲激情五月| 久久综合一区| 成人51免费| 91精品国产乱码久久久久久蜜臀| 国产九色在线| 精品福利一二区| 国产情侣免费视频| 亚洲专区一二三| 国产精品成人无码免费| 国产成人午夜99999| 黄色片久久久久| 五月开心六月丁香综合色啪| 精品国产_亚洲人成在线| 亚洲黑人在线| 全球成人中文在线| 国产白丝在线观看| 日韩中文字幕视频在线| 天堂a√在线| 日韩亚洲欧美中文三级| 小泽玛利亚一区二区三区视频| 一级精品视频在线观看宜春院 | 亚洲超碰97人人做人人爱| 国产又粗又黄又猛| 91色.com| 国产chinesehd精品露脸| 日韩电影在线观看一区| 国产精品一色哟哟| 亚洲综合激情在线| 天堂av一区二区| 亚洲传媒在线| 国产精品我不卡| 试看120秒一区二区三区| 国产精品一区二区久久久久| 亚洲少妇视频| 午夜精品一区二区三区视频免费看 | 国产乱人伦丫前精品视频| 国产欧美亚洲精品| 日本精品不卡| 777777777亚洲妇女| 美女尤物在线视频| 久久夜色撩人精品| 在线观看完整版免费| 亚洲午夜精品久久久久久久久久久久 | 激情成人综合网| 91 在线视频观看| 美女性感视频久久| www.色偷偷.com| 久久综合伊人| 99免费视频观看| 久久精品人人| 日本男人操女人| 老**午夜毛片一区二区三区| 日本日本19xxxⅹhd乱影响| 国产一区二区三区四区三区四| 秋霞在线一区二区| 亚洲欧美日韩高清在线| 中文字幕不卡每日更新1区2区| 日韩欧美大片| 在线视频欧美一区| 久久国产精品亚洲人一区二区三区| 性欧美.com| 99视频精品视频高清免费| 熟女熟妇伦久久影院毛片一区二区| 成人综合一区| 国产树林野战在线播放| 一区二区日韩欧美| 成人小视频在线观看免费| 亚洲精品韩国| 国产精品无码专区av在线播放 | 成人午夜影视| 中文字幕久热精品在线视频| 9色在线视频| 久久人体大胆视频| xxxcom在线观看| 日本伊人精品一区二区三区介绍| 久久野战av| 国产免费一区二区三区在线能观看 | 在线免费播放av| 久久综合久久综合亚洲| 精品成人av一区二区三区| 亚洲国产成人一区二区三区| 国产人与禽zoz0性伦| 一区二区三区波多野结衣在线观看 | 亚洲国产乱码最新视频| 欧美激情黑白配| 欧美日韩亚洲国产综合| 国产av无码专区亚洲av麻豆| 精品国产区一区| 精品久久av| 久久亚洲精品网站| 日韩av影片| 国产一区欧美二区三区| 国产精品对白| 日韩女优中文字幕| 欧美人成网站| av天堂永久资源网| 九九久久精品视频| 捆绑裸体绳奴bdsm亚洲| 国产精品伦一区| 国产精品.www| 欧美日韩一级黄| 蜜桃在线一区二区| 色爱精品视频一区| 波多野结依一区| 国产精品久久久久久久久久小说 | 蜜臀久久久99精品久久久久久| 久久aaaa片一区二区| 91免费观看在线| 日韩欧美123区| 在线看日本不卡| 欧美一区二区黄片| www日韩中文字幕在线看| 999福利在线视频| 91精品国产自产在线老师啪| 欧洲亚洲视频| 成年在线观看视频| 美国一区二区三区在线播放| 国产精品手机在线观看| 中文字幕一区二区三区四区不卡| 中文字幕亚洲精品在线| 91精品国产综合久久久久| 你懂的视频在线| 欧美激情伊人电影| 综合久久伊人| 亚洲精品成人自拍| 老鸭窝91久久精品色噜噜导演| 国产精品偷伦视频免费观看了| 国产精品免费丝袜| 国产伦精品一区二区三区视频网站| 欧美成va人片在线观看| 亚洲免费视频一区二区三区| 欧美综合在线观看| 国偷自产av一区二区三区| eeuss中文| 另类欧美日韩国产在线| 亚洲精品91在线| 一本色道**综合亚洲精品蜜桃冫| 欧美视频一二区| 久99九色视频在线观看| 在线播放成人| 中文字幕成人一区| 精品在线播放免费| 我想看黄色大片| 色94色欧美sute亚洲13| 日漫免费在线观看网站| 97在线看免费观看视频在线观看| 亚洲经典视频| 大陆极品少妇内射aaaaaa| 激情五月激情综合网| 林心如三级全黄裸体| 欧美视频你懂的| 国产黄在线观看| 国产精品成熟老女人| 国产成人精品免费视| 情侣黄网站免费看| 国产午夜三级一区二区三| 精品人妻一区二区三区潮喷在线| 精品无人区乱码1区2区3区在线| 久久男人天堂| 久久综合福利| 老司机午夜精品视频在线观看| 西西444www无码大胆| 色激情天天射综合网| 2017亚洲天堂1024| 国产日韩在线看| 9191国语精品高清在线| 超碰91在线播放| 亚洲一区二区偷拍精品| 日韩一区二区三区不卡| 欧美最猛性xxxxx(亚洲精品)| 亚洲深夜福利在线观看| 妺妺窝人体色www在线观看| 中文字幕乱码亚洲精品一区| 91tv国产成人福利| 欧美疯狂xxxx大交乱88av| 国产精品45p| 日av中文字幕| 自拍偷拍国产亚洲| 欧美自拍偷拍第一页| 91av视频在线免费观看| 精品香蕉视频| 日本黄色www| 一区二区免费在线| 完全免费av在线播放| 88xx成人永久免费观看| 五月天色一区| 国产伦精品一区二区三区免费迷 | 欧美日韩在线免费视频| 久草中文在线观看| 国产激情一区二区三区在线观看| 小嫩嫩精品导航| 亚洲欧美卡通动漫| 欧美va亚洲va| 日韩美女在线看免费观看| 宅男av一区二区三区| 成人av先锋影音| 中文区中文字幕免费看| 欧美激情影音先锋| sdde在线播放一区二区| av av在线| 欧美午夜精品电影| 国产www视频在线观看| 日本一区免费看| 国产成a人亚洲| 97人妻精品视频一区| 欧美激情精品久久久久久黑人| 国产影视一区| 精品人妻一区二区三| 在线精品视频小说1| 国产偷倩在线播放| 亚洲图片都市激情|