AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當前位置:首頁 > 科技  > 軟件

Gopher的Rust第一課:第一個Rust程序

來源: 責編: 時間:2024-06-07 17:18:43 163觀看
導讀經過上一章[1]的學習,我想現在你已經成功安裝好一個Rust開發環境了,是時候擼起袖子開始寫Rust代碼了!程序員這個歷史并不算悠久的行當,卻有著一個歷史悠久的傳統,那就是每種編程語言都將一個名為“hello, world”的示例作

經過上一章[1]的學習,我想現在你已經成功安裝好一個Rust開發環境了,是時候擼起袖子開始寫Rust代碼了!kam28資訊網——每日最新資訊28at.com

程序員這個歷史并不算悠久的行當,卻有著一個歷史悠久的傳統,那就是每種編程語言都將一個名為“hello, world”的示例作為這門語言學習的第一個例子,這個傳統始于20世紀70年代那本大名鼎鼎的由布萊恩·科尼根(Brian W. Kernighan)與C語言之父丹尼斯·里奇(Dennis M. Ritchie)合著的《C程序設計語言》。kam28資訊網——每日最新資訊28at.com

圖片圖片kam28資訊網——每日最新資訊28at.com

在這一章中,我們也將遵從傳統,從編寫和運行一個可以打印出“hello, world”的Rust示例程序開始我們正式的Rust編碼之旅。我希望通過這個示例程序你能夠對Rust程序結構有一個直觀且清晰的認識。kam28資訊網——每日最新資訊28at.com

3.1 Hello, World

“Hello, World”是一門編程語言的最簡單示例的表達形式。在Go中,我們可以像下面這樣編寫Go版本的Hello, World程序:kam28資訊網——每日最新資訊28at.com

package mainfunc main() {    println("Hello, World!")}

為了簡單,我們甚至沒有使用fmt包的Printf系列函數(這樣就可以減少一行導入包的語句),而是用了內置函數println來完成將“Hello, World”輸出到控制臺(更準確的說是標準錯誤(stderr))的任務。kam28資訊網——每日最新資訊28at.com

Rust版本的Hello, World可以比Go還要簡潔,我們在一個目錄下(比如rust-guide-for-gopher/helloworld/rustc)創建一個hello_world.rs的文件。哦,沒錯!rust的源碼文件都是以.rs作為源文件擴展名的。并且對于多個單詞構成的文件名,rust的慣例是采用全小寫單詞+下劃線連接的方式命名。這個hello_world.rs文件的內容如下:kam28資訊網——每日最新資訊28at.com

fn main() {    println!("Hello, World!");}

相比于Go在每個源文件中都要使用package指定該文件歸屬的包名,Rust無需這樣的一行。和Go一樣,這里的main是函數,所有可執行的Rust程序都必須有一個main函數,它是Rust程序的入口函數。和Go使用func函數聲明函數不同,Rust聲明函數的關鍵字為fn。在這個main函數中,我們調用println!將“Hello, World!”輸出到控制臺上。kam28資訊網——每日最新資訊28at.com

不過,和Go內置的println函數不同的是,這里的println!并非是一個函數,而是一個**Rust宏(macro)**。kam28資訊網——每日最新資訊28at.com

如果你只是學過Go,而沒有學過C/C++語言,你甚至都不會知道宏(macro)是什么。在Rust中,宏是一種用于代碼生成和轉換的元編程工具。宏允許你在編譯時根據一定的模式或規則來擴展代碼。Rust宏分為聲明宏(Declarative Macros)和過程宏(Procedural Macros)。println!就屬于聲明宏,它由macro_rules! 宏定義,我們在Rust標準庫的源碼中可以看到其定義:kam28資訊網——每日最新資訊28at.com

// $(rustc --print sysroot)/lib/rustlib/src/rust/library/std/src/macros.rs#[macro_export]#[stable(feature = "rust1", since = "1.0.0")]#[cfg_attr(not(test), rustc_diagnostic_item = "println_macro")]#[allow_internal_unstable(print_internals, format_args_nl)]macro_rules! println {    () => {        $crate::print!("/n")    };    ($($arg:tt)*) => {{        $crate::io::_print($crate::format_args_nl!($($arg)*));    }};}

在Rust源碼編譯過程中,聲明宏是在最開始的預處理階段進行擴展的,我們也可以通過nightly版的rustc命令來查看println!宏展開后的結果(-Z選項只能在nightly版本中使用):kam28資訊網——每日最新資訊28at.com

$rustc +nightly-2022-07-14-x86_64-apple-darwin  -Zunpretty=expanded  hello_world.rs#![feature(prelude_import)]#![no_std]#[prelude_import]use ::std::prelude::rust_2015::*;#[macro_use]extern crate std;fn main() {    {        ::std::io::_print(::core::fmt::Arguments::new_v1(&["Hello, World!/n"],                &[]));    };}

我們看到:println!宏被替換為一個標準庫下的函數(_print)的調用。btw,到這里,你可能和我一樣,看不懂println!展開后的代碼,沒關系,我們后續會逐步學習并掌握這些語法的。此外,宏是Rust的高級特性,這里也不展開說了。kam28資訊網——每日最新資訊28at.com

另外一個和Go在語法上有所不同的是,Rust在每行語句后面都要顯式使用分號,對于Gopher而言,這個很容易遺忘。kam28資訊網——每日最新資訊28at.com

接下來,我們來編譯和運行一下這個Rust版的Hello,World!,編譯運行Rust代碼的最簡單方法就是通過rustc編譯器將rust源碼文件編譯為可執行程序:kam28資訊網——每日最新資訊28at.com

$rustc hello_world.rs$lshello_world*  hello_world.rs

我們看到,示例通過調用rustc將hello_world.rs編譯為了hello_world可執行文件。kam28資訊網——每日最新資訊28at.com

運行rustc編譯后的可執行文件將得到下面輸出結果:kam28資訊網——每日最新資訊28at.com

$./hello_worldHello, World!

我們看到"Hello, World!"被打印到控制臺。kam28資訊網——每日最新資訊28at.com

如果覺得默認編譯出的hello_world文件名字較長,我們也可以像go build -o那樣指定rustc編譯后得到的目標可執行文件的名字,下面的命令通過-o選項將編譯后的程序命名為hello:kam28資訊網——每日最新資訊28at.com

$rustc -o hello hello_world.rs

rustc編譯出來的二進制文件size并不大,僅有400多KB(而Go默認構建的Hello, World!有1.3MB,在我的macOS上):kam28資訊網——每日最新資訊28at.com

$ls -lhtotal 856-rwxr-xr-x  1 tonybai  staff   423K  4 20 17:56 hello_world*

我們還可以通過去掉symbols的方式繼續讓其“瘦身”到不到300KB(通過go build -ldflags="-s -w" helloworld.go去除符號表和調試信息的Go二進制程序還有近900K的大小):kam28資訊網——每日最新資訊28at.com

$rustc -C strip=symbols hello_world.rs $ll -htotal 608-rwxr-xr-x  1 tonybai  staff   297K  4 20 17:57 hello_world*

上面的"Hello, World"程序雖然足夠簡單,也能夠運行,但對于初學者而言,它有兩個“不足”:一來這個例子的確“太簡單”,簡單到無法充分展示單個Rust源碼文件的結構;二來這個示例只使用了一個單個源文件,與實際開發中那種由多個文件組成的Rust實用工程有差別,同樣無法幫助我們理解實用性的Rust工程的結構。kam28資訊網——每日最新資訊28at.com

為了更好地理解Rust工程與單個源文件的構成,我們將編寫一個稍微復雜一點的版本,它將使用Rust的構建管理工具cargo建立,并使用Rust標準庫中的std::io模塊進行輸入/輸出操作。kam28資訊網——每日最新資訊28at.com

3.2 cargo版本的Hello, World

在實際開發中,Rust程序通常由多個源文件組成,并使用Cargo作為構建系統和包管理器。Cargo可以幫助我們管理項目的源代碼、依賴庫、構建任務等。下面我們就來創建一個使用Cargo的"Hello, World"。kam28資訊網——每日最新資訊28at.com

3.2.1 使用Cargo創建Hello,World

我們在一個目錄下(比如:rust-guide-for-gopher/helloworld/cargo)執行下面命令來創建hello_world:kam28資訊網——每日最新資訊28at.com

$cargo new hello_world    Created binary (application) `hello_world` package

cargo默認創建了一個binary(application)類型的rust package,我們來看看初始情況下這個rust package下都有哪些內容:kam28資訊網——每日最新資訊28at.com

$tree hello_world hello_world├── Cargo.toml└── src    └── main.rs1 directory, 2 files

其中,Cargo.toml是Rust包的清單(manifest)文件。它包含有關包及其依賴項的元數據。以下是上面Cargo.toml文件的全部內容:kam28資訊網——每日最新資訊28at.com

// Cargo.toml[package]name = "hello_world"version = "0.1.0"edition = "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]

其中package下面的字段含義如下:kam28資訊網——每日最新資訊28at.com

  • name: 包的名稱;
  • version: 包的版本,遵循語義化版本控制規則;
  • edition: 包使用的Rust版本(edition)。在這里,它被設置為目前的最新edition:2021版。edition提供了一種向后兼容的方式來演化和改進Rust。每個edition都是向后兼容的,這意味著舊edition下編寫的Rust代碼可以繼續在新edition版本的Rust下編譯和運行,而無需進行修改。這樣,開發者可以按照自己的節奏選擇是否遷移到新的edition。

dependencies下面則是會記錄該package對第三方依賴的情況,這個示例中并無三方依賴,因此這里為空。kam28資訊網——每日最新資訊28at.com

我們的代碼放在了src目錄下,這也是rust包的標準布局。為了更好地理解Rust程序的構成,我們將編寫一個稍微復雜一點的Hello, World!版本,它使用Rust標準庫中的std::io模塊進行輸入/輸出操作:kam28資訊網——每日最新資訊28at.com

// rust-guide-for-gopher/helloworld/cargo/hello_world/src/main.rsuse std::io;use std::io::Write;fn main() {    let mut output = io::stdout();    output.write(b"Hello, World!").unwrap();    output.flush().unwrap();}

這個Rust的"Hello, World"程序展示了一個典型的Rust源文件結構,包括導入語句、主函數定義以及一系列的方法調用。它演示了如何使用標準庫的io模塊來向標準輸出流打印"Hello, World!"。下面是對其程序結構的簡單總結:kam28資訊網——每日最新資訊28at.com

  • 導入語句

源文件在最開始處使用use std::io; 和use std::io::Write;這兩行導入了標準庫中的io模塊及其Write trait。這樣程序就可以在后面的代碼中直接使用io和Write,而無需完整地寫出它們的命名空間。這里我們先不用關心trait是什么,你大可將其理解為和Go interface差不多的語法元素就行了。kam28資訊網——每日最新資訊28at.com

  • 主函數

main定義了程序的入口點。Rust 程序從main函數開始執行。kam28資訊網——每日最新資訊28at.com

  • 可變變量

let mut output = io::stdout(); 這行代碼創建了一個可變變量output,它綁定到了一個標準輸出流(stdout)。mut關鍵字表示該變量是可變的,可以在后續代碼中修改它的值。關于變量以及綁定,我們在后面有專門的章節說明。這里要注意的是,和Go變量不同的是,Rust中的變量默認是不可變的,只有顯式用mut聲明的變量才是可變的。kam28資訊網——每日最新資訊28at.com

  • 方法調用

output.write(b"Hello, World!").unwrap(); 調用了output的write方法,傳遞了一個字節串作為參數。該方法用于將字節寫入輸出流。unwrap方法用于處理方法調用可能產生的錯誤,它在這里表示“我相信這個方法調用會成功,如果不成功,就讓程序 panic”。同理,output.flush().unwrap()也是這樣的。關于錯誤以及異常處理的話題,我們會在后面進行專題性學習。kam28資訊網——每日最新資訊28at.com

理解了源碼后,我們來編譯和運行一下這個程序,這次我們不再使用rustc,而是用cargo來實現。kam28資訊網——每日最新資訊28at.com

3.2.2 使用Cargo構建Hello, World

要構建上面的示例程序,我們只需在項目根目錄下運行下面命令:kam28資訊網——每日最新資訊28at.com

$cargo build   Compiling hello_world v0.1.0 (/Users/tonybai/Go/src/github.com/bigwhite/experiments/rust-guide-for-gopher/helloworld/cargo/hello_world)    Finished dev [unoptimized + debuginfo] target(s) in 1.23s

構建成功后,我們再來查看一下當前項目下的結構變化:kam28資訊網——每日最新資訊28at.com

$tree -F.├── Cargo.lock├── Cargo.toml├── src/│   └── main.rs└── target/    ├── CACHEDIR.TAG    └── debug/        ├── build/        ├── deps/        │   ├── hello_world-07284f5d84374479*        │   ├── hello_world-07284f5d84374479.1atc14vk0u28taij.rcgu.o        │   ├── hello_world-07284f5d84374479.1bu89c2i9mazzqif.rcgu.o        │   ├── hello_world-07284f5d84374479.26e3nxhmk9lhy9zy.rcgu.o        │   ├── hello_world-07284f5d84374479.29l81xyv0i4g8s88.rcgu.o        │   ├── hello_world-07284f5d84374479.41i7ln85cwseljfw.rcgu.o        │   ├── hello_world-07284f5d84374479.4iz3ubiqrvegnjdp.rcgu.o        │   ├── hello_world-07284f5d84374479.53vu8cjirf8g6rnw.rcgu.o        │   ├── hello_world-07284f5d84374479.5f6ye0ayl23rccqv.rcgu.o        │   └── hello_world-07284f5d84374479.d        ├── examples/        ├── hello_world*        ├── hello_world.d        └── incremental/            └── hello_world-16yuztatbr0vh/                ├── s-gvfwmugno5-1gy801r-1i2g78r4nmg489ix0nuktmqgb/                │   ├── 1atc14vk0u28taij.o                │   ├── 1bu89c2i9mazzqif.o                │   ├── 26e3nxhmk9lhy9zy.o                │   ├── 29l81xyv0i4g8s88.o                │   ├── 41i7ln85cwseljfw.o                │   ├── 4iz3ubiqrvegnjdp.o                │   ├── 53vu8cjirf8g6rnw.o                │   ├── 5f6ye0ayl23rccqv.o                │   ├── dep-graph.bin                │   ├── query-cache.bin                │   └── work-products.bin                └── s-gvfwmugno5-1gy801r.lock*9 directories, 28 files

我們看到cargo build執行后,項目下多出了好多目錄和文件。這些目錄和文件都是做什么的呢?我們挑選主要的來看一下。kam28資訊網——每日最新資訊28at.com

  • Cargo.lock文件

Cargo的鎖定文件,用于記錄每個依賴項的確切版本號,以保證構建的可重復性。kam28資訊網——每日最新資訊28at.com

這個示例中由于沒有使用第三方依賴,這個Cargo.lock文件中的內容不具典型性:kam28資訊網——每日最新資訊28at.com

# This file is automatically @generated by Cargo.# It is not intended for manual editing.version = 3[[package]]name = "hello_world"version = "0.1.0"

另外Cargo.lock文件完全由cargo自動管理,開發人員不需要也不應該對其進行手動修改。kam28資訊網——每日最新資訊28at.com

  • target目錄

存放構建輸出的目錄,用于存儲編譯后的目標文件和可執行文件。kam28資訊網——每日最新資訊28at.com

  • target/CACHEDIR.TAG

用于標記target目錄為一個緩存目錄的文件。它的內容如下:kam28資訊網——每日最新資訊28at.com

$cat CACHEDIR.TAG Signature: 8a477f597d28d172789f06886806bc55# This file is a cache directory tag created by cargo.# For information about cache directory tags see https://bford.info/cachedir/

這是一個符合Cache Directory Tagging Specification[2]的Tag文件。kam28資訊網——每日最新資訊28at.com

  • target/debug

調試模式下的構建輸出目錄,存儲生成的可執行文件和相關文件。kam28資訊網——每日最新資訊28at.com

  • target/debug/incremental

增量編譯的目錄,用于存儲增量編譯過程中的臨時文件和緩存。kam28資訊網——每日最新資訊28at.com

Rust編譯過程緩慢,這個對比Go簡直就是地下天上。在日常開發中,基于增量編譯的文件進行增量構建可以大幅縮短編譯時間。kam28資訊網——每日最新資訊28at.com

  • target/debug/build

編譯過程中生成的臨時構建文件的目錄。kam28資訊網——每日最新資訊28at.com

  • target/debug/deps

存儲編譯生成的目標文件(.o 文件)和相關的依賴項。kam28資訊網——每日最新資訊28at.com

  • target/debug/hello_world

調試模式下生成的可執行文件。kam28資訊網——每日最新資訊28at.com

  • target/debug/hello_world.d

與hello_world相關的依賴關系信息的文件。kam28資訊網——每日最新資訊28at.com

執行debug目錄下的hello_world將得到如下輸出:kam28資訊網——每日最新資訊28at.com

$./target/debug/hello_world Hello, World!

在Go中我們可以使用go run來直接編譯和運行Go源碼文件,cargo也提供了該功能,我們在項目根目錄下運行cargo run也可以編譯和執行hello_world:kam28資訊網——每日最新資訊28at.com

$cargo run    Finished dev [unoptimized + debuginfo] target(s) in 0.05s     Running `target/debug/hello_world`Hello, World!

無論是cargo run還是cargo build,默認構建的都是debug版本的可執行程序,程序中包含大量符號信息和調試信息,并且其優化級別也不是很高。發布到生產環境的程序應該是release模式下的,通過--release參數,我們可以構建release版本的可執行程序:kam28資訊網——每日最新資訊28at.com

$cargo build --release   Compiling hello_world v0.1.0 (/Users/tonybai/Go/src/github.com/bigwhite/experiments/rust-guide-for-gopher/helloworld/cargo/hello_world)    Finished release [optimized] target(s) in 1.06s

構建后,target目錄下會多出一個release目錄,其下面的內容如下:kam28資訊網——每日最新資訊28at.com

$tree -F target/release target/release├── build/├── deps/│   ├── hello_world-c41defdc625f9244*│   └── hello_world-c41defdc625f9244.d├── examples/├── hello_world*├── hello_world.d└── incremental/4 directories, 4 files

相對于debug版本,release版本由于實時了大量優化,通常其構建時間會比debug版本要長。但構建出的release版本的size則要小很多。kam28資訊網——每日最新資訊28at.com

無論是debug,還是release版,target下面都生成了許多中間文件,如果要清理文件并重頭構建,我們可以使用cargo clean命令將target徹底清除:kam28資訊網——每日最新資訊28at.com

$cargo clean     Removed 40 files, 2.1MiB total

當然cargo clean也支持一些命令行參數,可以選擇清除哪些文件。kam28資訊網——每日最新資訊28at.com

3.2.3 使用Cargo創建library類包

通過上面的例子,我們知道cargo new默認創建的binary類型的rust package,如果我們要創建library類型的rust package,我們需要向cargo new傳遞--lib選項。下面的命令創建一個名為foo的library類型的rust package:kam28資訊網——每日最新資訊28at.com

$cargo new --lib foo     Created library `foo` package

我們看一下foo package下的目錄結構:kam28資訊網——每日最新資訊28at.com

$tree -F foofoo├── Cargo.toml└── src/    └── lib.rs1 directory, 2 files

和binary類不同的是,src目錄下不再是main.rs,而是lib.rs,它是library類package的入口:kam28資訊網——每日最新資訊28at.com

//rust-guide-for-gopher/helloworld/cargo/foo/lib.rspub fn add(left: usize, right: usize) -> usize {    left + right}#[cfg(test)]mod tests {    use super::*;    #[test]    fn it_works() {        let result = add(2, 2);        assert_eq!(result, 4);    }}

lib.rs中只是一個library類package的入口模板,開發人員需要根據自己的需要對其進行調整。關于lib.rs中的內容,我們將在下一章講解Rust代碼組織時做細致說明,這里就不展開說了。kam28資訊網——每日最新資訊28at.com

對于library類Rust package,我們同樣可以通過cargo build和cargo build --release構建,下面是執行構建后目錄文件情況:kam28資訊網——每日最新資訊28at.com

$tree.├── Cargo.lock├── Cargo.toml├── src│   └── lib.rs└── target    ├── CACHEDIR.TAG    ├── debug    │   ├── build    │   ├── deps    │   │   ├── foo-24c6d6228c521501.2k5t0f94hnorqpgh.rcgu.o    │   │   ├── foo-24c6d6228c521501.d    │   │   ├── libfoo-24c6d6228c521501.rlib    │   │   └── libfoo-24c6d6228c521501.rmeta    │   ├── examples    │   ├── incremental    │   │   └── foo-m2biu8poxl6i    │   │       ├── s-gvg68shtlp-1oqrf4n-irxhgoe7rhwmtvj6jwexcu0h    │   │       │   ├── 2k5t0f94hnorqpgh.o    │   │       │   ├── dep-graph.bin    │   │       │   ├── query-cache.bin    │   │       │   └── work-products.bin    │   │       └── s-gvg68shtlp-1oqrf4n.lock    │   ├── libfoo.d    │   └── libfoo.rlib    └── release        ├── build        ├── deps        │   ├── foo-9f2dd76beda509bd.d        │   ├── libfoo-9f2dd76beda509bd.rlib        │   └── libfoo-9f2dd76beda509bd.rmeta        ├── examples        ├── incremental        ├── libfoo.d        └── libfoo.rlib14 directories, 20 files

我們看到,無論是debug還是release,cargo build構建的結果都是libfoo.rlib。.rlib文件是Rust的靜態庫文件,通常用于代碼的模塊化和重用,我們在后續章節講解中,會詳細說明如何使用這些構建出來的靜態庫。kam28資訊網——每日最新資訊28at.com

3.3 小結

本文介紹了如何使用Rust編寫"Hello, World"程序,并分別給出了rustc版和cargo版的hello, world程序版本。kam28資訊網——每日最新資訊28at.com

在這個過程中,文章還介紹了Rust中的宏概念,并展示了如何使用println!宏來輸出文本。kam28資訊網——每日最新資訊28at.com

之后,文章聚焦于使用Cargo構建的hello,world程序版本,介紹了cargo的構建、清理、debug和release版本的區別等,最后還提及了如何使用cargo創建library類的Rust package。kam28資訊網——每日最新資訊28at.com

cargo貫穿Rust程序的整個生命周期,在后續的每一章中可能都會提及cargo。kam28資訊網——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-92740-0.htmlGopher的Rust第一課:第一個Rust程序

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: SpringBoot項目保證接口冪等的五種方法!

下一篇: 克服403錯誤:Python爬蟲的反爬蟲機制應對指南

標簽:
  • 熱門焦點
Top