來玩 Rust 的框架吧! - Rocket - Part II
昨天我們用已經用 Rocket 建立了一個 server,並且可以接收回傳 JSON,今天就來了解一下怎麼使用 Rocket 建立一個 RESTful API。
RESTful API我們會建立一個簡單的 Restful API 的範例,分別會用到 GET、POST、PUT、DELETE 這四個 HTTP method,接下來我們就來看看怎麼實作。
分別建立以下的函式,這邊使用 #[get]、#[post]、#[put]、#[delete] 來指定 HTTP method:
123456789101112131415161718192021222324#[get("/demo")]fn get_demo() -> Value { json!([{ "id": 1, "name": "Bucky" }, { "id": 2, "name": "Tom" }])}#[get(&qu ...
來玩 Rust 的框架吧! - Rocket - Part I
今天開始我們將會開始使用 Rust 的 Web 框架 – Rocket,來建立一個簡單的 Web Server。
Rocket 是什麼?簡單介紹一下 Rocket,Rocket 是一個基於 Rust 語言的一個 Web 框架,截止目前為止在 GitHub 上一共有 18.6k 的星星數。
Rocket 的設計靈感來自於 Ruby on Rails、Flask等知名的 Web 框架,並且在設計上有著類似的特性,並以 3 個核心概念來設計:
安全、正確,還有重視開發者體驗。
所有的請求都會由 Rocket 來處理型別。
不強制使用 Rocket 的函式庫,可以讓開發者自由選擇。
然後個人覺得 Rocket 的文件寫的非常的完整,算是蠻適合新手入門的一個框架。
從 Hello World 開始那麼我們就從一個簡單的 Hello World 開始,首先我們先建立一個 binary-based 的新專案:
1$ cargo new --bin rocket-server
安裝 Rocket在 Rust 安裝套件非常容易,只要在 Cargo.toml 裡面 [dependencies] 的下方 ...
來用 Rust 建立一個伺服器吧!Part IV
昨天我們新增了一個檔案,專門來做執行緒池的部分,但還沒完成,所以今天就接著繼續完成。
建立執行緒池的儲存空間首先我們要建立一個儲存執行緒的空間,這個空間會儲存所有的執行緒,然後我們可以從這個空間取出執行緒來使用。
12345678910111213141516171819202122use std::thread::JoinHandle;pub struct ThreadPool { threads: Vec<JoinHandle<()>>,}impl ThreadPool { pub fn new(size: usize) -> ThreadPool { assert!(size > 0); let mut threads = Vec::with_capacity(size); for _ in 0..size { // 產生執行緒並儲存至向量 } ThreadPool { t ...
來用 Rust 建立一個伺服器吧!Part III
在開始建立多執行緒伺服器之前,我們先來模擬一下單一執行緒伺服器會遇到的問題。
模擬請求問題直接上 code 吧!
123456789101112131415161718use std::thread; // 引入 thread 模組use std::time::Duration; // 引入 Duration 模組// 省略...fn handle_connection(mut stream: TcpStream) { // 省略... let delay = b"GET /delay HTTP/1.1\r\n"; let (status_line, path) = if buffer.starts_with(get) { ("HTTP/1.1 200 OK", Path::new("./src/HTML/hello.html")) } else if buffer.starts_with(delay) { thread::sleep( ...
來用 Rust 建立一個伺服器吧!Part II
昨天我們成功讀取了 request,今天就繼續接著做,首先來處理發送 response 的部分。
發送 response我們可以利用 TcpStream 的 write 方法來發送 response:
1234567891011fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 512]; let c = stream.read(&mut buffer).unwrap(); println!("請求內容: {}", String::from_utf8_lossy(&buffer[..c])); let response = "HTTP/1.1 200 OK\r\n\r\n"; stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap();}
這裡我們先宣告了一個 response 變數,裡面放的是一個 ...
來用 Rust 建立一個伺服器吧!Part I
從今天開始,我們要開始實作一個簡單的 HTTP 伺服器,並且複習一下並且運用我們之前學到 Rust 的知識,然後視情況加入一些需要了解的部分,到時候再加入解說,那麼就開始吧!
建立一個新專案首先,我們要建立一個新的專案,這邊我們使用 cargo 來建立一個新的專案,使用 cargo new 指令,並且指定專案名稱為 server:
1$ cargo new server
監聽 TCP 連線接著,我們要開始實作一個 TCP 伺服器,並且監聽 TCP 連線。Rust 的標準函式庫中有一個 std::net 模組,裡面有一個 TcpListener 型態,可以用來監聽 TCP 連線,所以我們可以先在 src/main.rs 中 使用 TcpListener:
12345678910111213use std::net::TcpListener; const PORT: i32 = 3000; fn main() { let listener = TcpListener::bind(format!("127.0.0.1:{}", POR ...
Rust 的模組
因為我們明天將會開始試著建立一個專案來玩玩看,但在此之前我們要先了解一下模組的概念。
什麼是模組(module)?在使用 Cargo 建立的專案中,會像下方這樣,所有程式都需要經由 main 來做處理。但是我們當然不可能把所有邏輯都寫在同一個檔案中,這樣子會非常難維護。
1234project|__Cargo.toml|__src |__main.rs
所以我們可以透過模組化的方式來管理專案,最後再經由 main 來處理。我們來做一個示範,首先在 /src 路徑下新增一個 lib.rs,裡面我們建立一個函式 say_hi, 裡面我們寫了一段程式來印出 “Hi!”。
123fn say_hi() { println!("Hi!");}
接著在 main 裡面引入剛剛建立的 say_hi。寫法是先寫這個專案的名字(不是這個專案資料夾名字,而是指 Cargo.toml 裡面的 package name),這個專案的名稱是 hello_world,接著加上一個標識符 ::,最後再加上要引入的函式名稱即可,就像下方範例:
123fn main() ...
Rust 的生命週期
今天我們要來介紹其他程式語言中比較少見的機制,但是在 Rust 中是屬於和參考(reference)有關的機制,那就是生命週期(lifetime)。
一、生命週期的概念生命週期是 Rust 中的一個概念,它是一個變數的有效範圍,也就是它可以被使用的範圍。生命週期的概念是為了解決 Rust 中的參考的問題,因為參考是 Rust 中的一個重要機制,它可以讓開發者在不需要複製資料的情況下,就可以使用資料。但是參考也有一個問題,就是它的生命週期,也就是它的有效範圍,如果參考的資料已經被釋放了,那麼參考就會變成一個無效的參考,這樣就會造成程式的錯誤。
這裡有一個範例:
12345678910{ let r; { let x = 5; r = &x; } println!("r: {}", r);}
先說結論,這個範例是沒辦法通過編譯的,也就是會報錯。這是因為我們先宣告了一個變數 r,然後我們在一個新的區塊中宣告了一個變數 x,並且將 x 的參考賦值給 r。 ...
Rust 的閉包
什麼是閉包(closure)?Rust 的閉包是一種匿名函式,可以從其函式的作用域中捕獲數值,而且閉包是為了快速執行而設計的,也因為如此它們的性能比一般函式還要好。
閉包有以下幾個特性:
是一種匿名函式
可以保存為變數,或是作為參數傳遞給其他函式
可以在某處建立閉包,然後在不同的地方執行它
可以從定義的作用域中捕獲變數
如何建立閉包?閉包的建立方式:
使用 | 來分隔參數,| 後面是函式的主體
使用 {} 來包裹函式的主體
例如:
12let plus_one = |x: i32| -> i32 { x + 1 };plus_one(1); // 2
建立閉包的時候,有些特殊的地方可以注意:
可以不用參數,直接使用 || 來建立一個空閉包
可以不用 return 來回傳值,直接使用 -> 來指定回傳值的型別
可以不用指定型別,Rust 會自動推導型別
如何使用閉包?上面的範例中,我們建立了一個閉包,並且將它存到變數 plus_one 中,然後呼叫它。
閉包可以像函式一樣使用,例如:
12345fn main() ...
Rust 的枚舉
枚舉(Enums)枚舉是一種定義一組可能值的方法,這些值被稱為成員(variants)。枚舉的每個成員都是一個獨立的類型,並且可以是不同類型的值,包括整數、浮點數、字符串、元組、數組、結構體等。
123456enum PhoneSystem { Android, Ios, Windows, Linux,}
像上面的例子,首先使用 enum 關鍵字定義了一個名為 PhoneSystem 的枚舉,命名規則是大寫開頭的駝峰式命名法。
前面提到枚舉的每一個成員都可以是不同類型的值,這讓枚舉在使用上非常靈活,不過還是有一些限制,比如說,枚舉的成員不能重複,也不能是空的。
1234567enum PhoneSystem { Android, Ios, Windows, Linux, Android, // error: duplicate variant `Android`}
123enum PhoneSystem { // error: empty enum is not allow ...