Rust Introduction

Rust is a systems-level programming language led by Mozilla Research, withmemory safety、zero-cost abstractionsandfearless concurrencyas its core design goals.

Rust was born in 2006, initially as a personal project of Mozilla employee Graydon Hoare, and later in 2009 it was officially sponsored by Mozilla, which formed a team to develop it.

Rust's original design goal was to solve the long-standing pain points of C/C++ in systems programming:memory safety issues(null pointers, dangling pointers, buffer overflows) andthe complexity of concurrent programming(data races, deadlocks).

Rust's design philosophy is to "eliminate bugs at compile time"—through a powerful type system and ownership mechanism, the compiler helps you catch memory errors and data races instead of discovering problems at runtime.

The Rust language mascot is a crab named Ferris, friendly and cute, representing the friendly and inclusive culture of the Rust community.

Rust's key design goals can be summarized as follows:

Design GoalDescription
Memory Safety (No GC)Guarantees memory safety at compile time through the ownership system, without needing a garbage collector
Zero-Cost AbstractionsHigh-level language features have no runtime overhead after compilation, matching the performance of hand-written low-level code
Fearless ConcurrencyThe type system and ownership rules eliminate data races at compile time
PracticalityCan write both operating system kernels and web applications, covering full-stack scenarios
Strong Type InferenceThe compiler automatically infers most types, reducing boilerplate code
Rich ToolchainCargo (package management + build), rustfmt (formatting), clippy (linting) ready to use out of the box

History of the Rust Language

Rust has gone through nearly two decades of development, from a personal project to a widely adopted systems language worldwide.

TimeVersion/EventDescription
2006Personal ProjectGraydon Hoare began designing the Rust language
2009Mozilla SponsorshipMozilla officially supported Rust development and formed a team
2010First Public AnnouncementFirst publicly revealed the Rust compiler (written in OCaml) at a Mozilla summit
2011Bootstrapping CompletedThe Rust compiler was rewritten in Rust itself, eliminating the dependency on OCaml
May 2015Rust 1.0First stable version released, marking the language officially ready for production use
December 2018Rust 2018 EditionFirst Edition released, optimizing syntax and module system (introducing NLL borrow checker)
2019Async Ecosystem Takes Shapeasync/await syntax stabilized, async runtimes like Tokio matured
2021Rust Foundation EstablishedAWS, Google, Huawei, Microsoft, and Mozilla jointly established the Rust Foundation
October 2021Rust 2021 EditionIntroduced improvements such as disjoint capture and IntoIterator for arrays
2022Adopted by the Linux KernelLinux 6.1 officially merged Rust support, making Rust the second language for kernel development
February 2024Rust 2024 EditionFurther optimized language consistency and developer experience

The founding of the Rust Foundation in 2021 was a milestone. Five tech giants—AWS, Google, Huawei, Microsoft, and Mozilla—jointly committed to ensuring Rust's long-term independent development, alleviating community concerns about "domination by a single company."

Rust adoptsa stable release every six weeksrelease cadence, while also usingEditionmechanism (released approximately every 3 years) to handle incompatible language changes. A single project can mix crates from different Editions, and the compiler guarantees backward compatibility.


Core Features of the Rust Language

Rust's feature set revolves around three dimensions: "safe, efficient, practical." Below we break down Rust's most core language features one by one.

Ownership System

Ownership is Rust's most unique and important feature. It manages memory at compile time, without a garbage collector or manual malloc/free.

Ownership has three major rules:

RuleDescriptionMeaning
Every value has exactly one ownerA value can be owned by only one variable at a timeEliminates double-free and dangling pointers
When the owner leaves scope, the value is droppedWhen a variable leaves its scope, Rust automatically calls drop to free memoryNo need for manual memory management
At any time: either one mutable reference, or multiple immutable referencesMutually exclusive read/write, coexisting read/readEliminates data races at compile time

The intuitive experience at the code level: when a variable is assigned to another variable, ownership is transferred (move), and the original variable becomes invalid.

Example

fn main() {
    // s1 gains ownership of the string (data stored on the heap)
    let s1 = String::from("Hello, EXAMPLE!");

    // Ownership moves from s1 to s2; after this, s1 is no longer valid
    let s2 = s1;

    // println!("{}", s1); // Compile error! s1 has been moved
    println!("{}", s2);    // Correct: s2 is now the owner

    // Basic types (stored on the stack) implement the Copy trait, so no move occurs
    let x = 42;
    let y = x;  // x is still valid because i32 implements Copy
    println!("x = {}, y = {}", x, y); // Both can be used

    // Immutable references: multiple can exist at the same time
    let s3 = &s2; // Immutable borrow
    let s4 = &s2; // Another immutable borrow, allowed
    println!("s3 = {}, s4 = {}", s3, s4);

    // Mutable reference: only one can exist at a time
    let mut data = String::from("example");
    let r = &mut data; // Mutable borrow
    r.push_str("!");   // Modify the original value through a mutable reference
    println!("After modification: {}", r);
    // The scopes of s3 and s4 have ended, so mutable references can be created
}

The ownership system is a "steep slope" in Rust's learning curve. Beginners often "fight" with the compiler, but once understood, you'll find that the errors caught by the compiler could be fatal runtime bugs in C/C++.

Pattern Matching

Rust'smatchexpressions are extremely powerful; the compiler willexhaustively checkall cases, ensuring no branch is missed.

Example

fn main() {
    // match with number matching
    let score = 85;
    match score {
        90..=100 => println!("EXAMPLE Rating: A"), // Range matching
        60..=89  => println!("EXAMPLE Rating: B"),
        0..=59   => println!("EXAMPLE Rating: C"),
        _ => println!("Invalid score"),                // Wildcard, matches all remaining cases
    }

    // match destructuring a tuple
    let pair = (3, 7);
    match pair {
        (0, y) => println!("First is 0, second is {}", y),
        (x, 0) => println!("First is {}, second is 0", x),
        (x, y) => println!("Two values: {} and {}", x, y),
    }
}

Enums and Option/Result

Rust's enums can carry data, and combined with match they enable safe and expressive control flow.

In the standard library,Option<T>andResult<T, E>are the two most core enum types, handling "possibly absent" and "possibly erroneous" scenarios respectively—Rust has no null or exceptions.

Example

use std::fs;

fn main() {
    // Option<T>: a value may or may not exist (replaces null)
    let numbers = vec![10, 20, 30];
    let first = numbers.get(0);   // Returns Option<&i32>
    let missing = numbers.get(5); // Returns Option<&i32>

    match first {
        Some(&val) => println!("First element: {}", val),
        None => println!("Not found"),
    }

    // More concise way: if let
    if let Some(&val) = missing {
        println!("Found: {}", val);
    } else {
        println!("Index 5 does not exist");  // This branch is expected
    }

    // Result<T, E>: an operation may succeed or fail (replaces exceptions)
    let content = fs::read_to_string("config.txt");
    match content {
        Ok(text) => println!("File content: {}", text),
        Err(e) => println!("Read failed: {}", e),
    }
}

Rust has no null values and no try-catch exception mechanism. Option and Result are enforced at compile time—if you forget to check, the code won't compile. This fundamentally eliminates null pointer exceptions and uncaught exceptions.

Zero-Cost Abstractions

Rust's high-level features (iterators, closures, generics) are completely equivalent to hand-written low-level loops after compilation, with no additional runtime overhead.

This means you can write code in a functional style while achieving performance as high as C.

Example

fn main() {
    let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    // Iterator chaining — looks like a high-level abstraction
    // Expands after compilation into machine code equivalent to a hand-written for loop
    let result: Vec<i32> = data
        .iter()            // Get an iterator
        .filter(|&x| x % 2 == 0) // Filter even numbers
        .map(|&x| x * x)         // Square
        .collect();              // Collect into a Vec

    println!("{:?}", result); // [4, 16, 36, 64, 100]
}

Fearless Concurrency

Rust's type system and ownership rulesat compile timeeliminate data races. The Send and Sync traits automatically mark whether a type can be safely transferred or shared between threads.

If you try to share non-thread-safe data across threads, the compiler will directly report an error—rather than waiting for weird concurrency bugs to appear in production at runtime.

Example

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    // Arc (atomic reference counting) is used to share ownership across threads
    // Mutex provides mutually exclusive access
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for i in 0..10 {
        // Clone Arc so each thread holds a reference to the same data
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            // lock() acquires the mutex and returns MutexGuard
            let mut num = counter.lock().unwrap();
            *num += 1; // Modify the inner value through dereferencing
            println!("Thread {} finished accumulating, current value: {}", i, *num);
        });
        handles.push(handle);
    }

    // Wait for all threads to finish
    for handle in handles {
        handle.join().unwrap();
    }

    println!("Final count: {}", *counter.lock().unwrap());
}

Cargo: All-in-One Build Tool

Cargo is Rust's official package manager and build system, integrating dependency management, compilation, testing, documentation generation, and publishing.

Compared with the fragmented experience in the C/C++ ecosystem requiring manual configuration of CMake/Makefile and package managers, Cargo provides an "out-of-the-box" all-in-one experience.

CommandFunctionTypical Scenario
cargo new projectCreate a new projectInitialize a project, automatically generating Cargo.toml and src/main.rs
cargo buildCompile the project (debug mode)Development and debugging
cargo build --releaseCompile the project (optimized mode)Production release, enabling all compiler optimizations
cargo runCompile and runQuickly test code
cargo testRun testsUnit tests, integration tests, doc tests
cargo checkQuickly check whether the code compiles (without generating a binary)Real-time syntax checking in IDEs
cargo doc --openGenerate and open documentationBrowse API documentation of dependency libraries
cargo clippyCode style checking (lint)Detect discouraged practices and potential issues
cargo fmtCode formattingUnify code style
cargo publishPublish to crates.ioPublish an open-source library

cargo check is a very useful command during development. It only checks syntax and types and doesn't generate an executable, so it's much faster than cargo build. Most IDE Rust plugins automatically run cargo check when saving files.


Application Areas of the Rust Language

With its memory safety and high performance, Rust is rapidly expanding across multiple domains.

Systems Programming and High-Performance Servers

This is Rust's "home turf"—suitable for scenarios requiring extreme performance and high memory safety. Asynchronous runtimeTokioand web frameworksActix Web、Axumexcel in high-concurrency scenarios.

WebAssembly

Rust isone of the best languages for compiling to WebAssembly. Its zero-cost abstractions and GC-free nature make Wasm artifacts small and high-performance.

Frontend frameworksYewandLeptosallow writing SPA applications in Rust. Figma, Cloudflare Workers, etc. heavily use Rust + Wasm in production.

Embedded and IoT

Rust'sno_stdmode allows running on bare metal without an operating system, suitable for resource-constrained devices like MCUs and sensors.

RTIC(real-time interrupt-driven concurrency framework) andEmbassy(asynchronous embedded framework) are representative projects of embedded Rust.

CLI Tools

Rust compiles to standalone binaries with excellent performance, producing many star CLI tools:

ToolFunctionReplaces
ripgrep (rg)Ultra-fast code searchgrep
fdQuick file searchfind
batFile viewing with syntax highlightingcat
deltaGit diff with syntax highlightinggit diff
zoxideSmart directory navigationcd
dustDisk space analysisdu

Blockchain and Web3

Rust holds an important position in the blockchain field.SolanaThe core of the chain is written in Rust,Sui、Aptos、Polkadot(Substrate)and other new public chains also use Rust as their primary development language.

Databases and Data Infrastructure

High-performance databases and data processing systems are areas of strength for Rust:TiKV(distributed KV storage, TiDB's storage layer),SurrealDB、Polars(DataFrame library),Databend(cloud data warehouse), etc., are all built with Rust.

Operating Systems and Infrastructure

Rust has entered the Linux kernel (6.1+). Google is rewriting Bluetooth and Wi-Fi stacks with Rust in Android, and Microsoft is also rewriting some components in the Windows kernel using Rust.

For hobby projects,Redox OSis a Unix-like operating system written entirely from scratch in Rust.


Quick Start

Rust officially provides an extremely convenient installation tool, rustup, which handles the compiler, package manager, and documentation with a single command.

Installing Rust

# Unix/macOS
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 验证安装
$ rustc --version
rustc 1.85.0 (4d91de4e4 2025-02-17)

$ cargo --version
cargo 1.85.0

Your First Rust Program

Using Cargo to create a project is the most recommended approach; it automatically generates a standard project structure.

# 创建新项目
$ cargo new hello_example
     Created binary (application) `hello_example` package

# 项目结构
hello_example/
├── Cargo.toml     # 项目元数据和依赖配置
└── src/
    └── main.rs    # 程序入口文件

Cargo.toml is the configuration file for the project, similar to Node.js's package.json:

Example

[package]
name = "hello_example"           # Package name (required, separated by underscores)
version = "0.1.0"               # Version number (required, follows semantic versioning)
edition = "2024"                # Rust Edition (required, affects language syntax rules)

[dependencies]
# Declare project dependencies here, for example:
# serde = { version = "1.0", features = ["derive"] }

The generated main.rs by default contains a simple Hello World program:

Example

// File path: src/main.rs
// Rust program entry point: every executable program must have a main function

fn main() {
    // println! is a macro (note the ! symbol after it)
    // Macros expand into more complex code at compile time
    println!("Hello, EXAMPLE!");
    println!("Welcome to the world of the Rust language");
}
# 编译并运行
$ cargo run
   Compiling hello_example v0.1.0
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.52s
     Running `target/debug/hello_example`
Hello, EXAMPLE!
欢迎来到 Rust 语言的世界

Basic Syntax Overview

Rust's syntax borrows the strengths of functional languages and systems languages. The following is a quick overview of the most commonly used syntax elements; each example is a complete program that can be run directly.

Variables and Mutability

Variables in RustImmutable by default(immutable). This forces developers to explicitly mark which data can change, making the code's intent clearer and making it easier for the compiler to optimize.

Example

fn main() {
    // Immutable variable (default): cannot be reassigned after declaration
    let name = "EXAMPLE";
    // name = "other"; // Compile error! Immutable variables cannot be reassigned
    println!("Name: {}", name);

    // Mutable variable: explicitly declared with the mut keyword
    let mut score = 0;
    println!("Initial score: {}", score);
    score = 100;       // Can be modified
    println!("Updated score: {}", score);

    // Variable shadowing: redeclare a variable with the same name using let
    let x = 5;
    let x = x + 1;      // The new variable x shadows the old x, with a value of 6
    let x = x * 2;      // Shadowed again, with a value of 12
    println!("Final value of x: {}", x);

    // Constant: determined at compile time, type must be annotated, naming convention uses all uppercase
    const MAX_POINTS: u32 = 100_000;
    println!("Maximum points: {}", MAX_POINTS);
}

Variable shadowing is not the same as mut. Shadowing uses let to redeclare a new variable with the same name, which can change the type; mut simply allows the value of the same variable to be modified, with the type unchanged.

Basic Data Types

Rust is a statically typed language; the compiler must know the type of every variable at compile time.

CategoryTypeExampleDescription
Signed integersi8, i16, i32, i64, i128, isizelet x: i32 = -42;The default integer type is i32
Unsigned integersu8, u16, u32, u64, u128, usizelet y: u64 = 100;usize is used for indexing and lengths
Floating-point numbersf32, f64let pi: f64 = 3.14;The default floating-point type is f64
Booleanboollet ok: bool = true;true or false
Charactercharlet c = 'R';4 bytes, representing a Unicode scalar value
Tuple(T1, T2, ...)let t: (i32, f64) = (10, 3.14);Fixed length, elements can have different types
Array[T; N]let a: [i32; 3] = [1, 2, 3];Fixed length, same type, allocated on the stack
Slice&[T]let s: &[i32] = &a[..];A reference view into an array or Vec
Dynamic arrayVec<T>let v: Vec<i32> = vec![1,2,3];Variable length, allocated on the heap
StringString / &strlet s = String::from("hi");String is mutable, &str is a reference

Control Flow

Rust's control flow includesif、loop、while、forandmatch. Among them, if and loop can return values as expressions.

Example

fn main() {
    let score = 75;

    // if is an expression and can return a value (all branches must return the same type)
    let grade = if score >= 90 {
        "A"
    } else if score >= 60 {
        "B"
    } else {
        "C"
    };
    println!("EXAMPLE rating: {}", grade);

    // loop is an infinite loop (exit with break, can return a value)
    let mut count = 0;
    let result = loop {
        count += 1;
        if count == 5 {
            break count * 2; // break followed by a value acts as the loop's return value
        }
    };
    println!("loop return value: {}", result);

    // while conditional loop
    let mut n = 3;
    while n > 0 {
        println!("Countdown: {}", n);
        n -= 1;
    }

    // for in iteration (most commonly used)
    let items = ["apple", "banana", "cherry"];
    for (i, item) in items.iter().enumerate() {
        println!("Item {}: {}", i + 1, item);
    }

    // for in range iteration
    for num in 1..=3 { // 1..=3 represents 1, 2, 3 (inclusive of both ends)
        println!("Number: {}", num);
    }
}

Functions

Rust functions usefnkeyword to declare, and parameters and return values must be explicitly annotated with types. The value of the last expression is the return value (no return keyword needed).

Example

// Ordinary function: takes two i32s, returns their sum
fn add(a: i32, b: i32) -> i32 {
    a + b  // The last expression is automatically used as the return value (note there is no semicolon at the end)
}

// Multiple return values: use a tuple to return several values
fn divide(a: f64, b: f64) -> (f64, String) {
    if b == 0.0 {
        return (0.0, String::from("Error: divisor cannot be zero"));
    }
    (a / b, String::from("OK")) // Returns a tuple
}

// Generic function: uses trait bounds to constrain the behavior of type parameters
fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}

fn main() {
    // Calling an ordinary function
    let sum = add(10, 20);
    println!("10 + 20 = {}", sum);

    // Calling the multiple-return-value function
    let (result, msg) = divide(10.0, 2.0);
    println!("10 / 2 = {} ({})", result, msg);

    // Division by zero case
    let (result, msg) = divide(10.0, 0.0);
    println!("10 / 0 = {} ({})", result, msg);

    // Calling a generic function
    let nums = vec![3, 7, 2, 9, 5];
    println!("EXAMPLE maximum number: {}", largest(&nums));
}

Structs and impl Blocks

Rust usesstructto define data structures, and usesimplblocks to implement methods for structs. This is similar to Go's method definition style, but the language organization is more compact.

Example

// Defines a struct (similar to C's struct, but more powerful)
struct User {
    name: String,         // String type, owns the data
    email: String,
    active: bool,
    login_count: u64,     // u64 is an unsigned 64-bit integer
}

// impl block implements methods for User
impl User {
    // Associated function (like a static method): creates a new instance
    fn new(name: String, email: String) -> User {
        User {
            name,
            email,
            active: true,     // Default value
            login_count: 0,   // Default value
        }
    }

    // Method: &self represents an immutable reference to the instance
    fn summary(&self) -> String {
        format!("{} ({}) - Login count: {}", self.name, self.email, self.login_count)
    }

    // Method: &mut self represents a mutable reference to the instance
    fn login(&mut self) {
        self.login_count += 1;
    }
}

fn main() {
    // Creates an instance using an associated function
    let mut user = User::new(
        String::from("example"),
        String::from("example@example.com"),
    );

    println!("{}", user.summary());

    // Simulates login
    user.login();
    user.login();
    println!("Logged in {} times", user.login_count);
}

Common Examples

The following two complete examples close to real-world scenarios demonstrate typical uses of Rust in actual development.

Building an HTTP API Service

Use the Axum framework (based on the Tokio async runtime) to build an HTTP API with JSON responses.

First, add the dependencies to Cargo.toml:

# Cargo.toml
[dependencies]
axum = "0.8"              # Web 框架
tokio = { version = "1", features = ["full"] }  # 异步运行时
serde = { version = "1", features = ["derive"] } # 序列化
serde_json = "1"           # JSON 支持

Example

// File path: src/main.rs
use axum::{
    extract::Query,
    response::Json,
    routing::get,
    Router,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

// Request parameter struct (deserialization)
#[derive(Deserialize)]
struct HelloParams {
    name: Option<String>, // Option indicates an optional parameter
}

// Response struct (serialization)
#[derive(Serialize)]
struct ApiResponse {
    code: u16,
    message: String,
    data: Option<HashMap<String, String>>, // Optional data field
}

// Handles GET /hello requests
// Query(HelloParams) automatically extracts name from URL query parameters
async fn hello_handler(Query(params): Query<HelloParams>) -> Json<ApiResponse> {
    let name = params.name.unwrap_or_else(|| String::from("EXAMPLE"));

    let mut data = HashMap::new();
    data.insert("name".to_string(), name.clone());

    Json(ApiResponse {
        code: 200,
        message: format!("Hello, {}! Welcome to the Rust HTTP service", name),
        data: Some(data),
    })
}

// The #[tokio::main] macro converts async fn main into a Tokio runtime
#[tokio::main]
async fn main() {
    // Building the router
    let app = Router::new()
        .route("/hello", get(hello_handler));

    // Binds the address and starts the service
    let listener = tokio::net::TcpListener::bind("0.0.0.0:8080")
        .await
        .unwrap();

    println!("EXAMPLE server started, listening at: http://localhost:8080");
    println!("Access example: http://localhost:8080/hello?name=Rust开发者");

    axum::serve(listener, app).await.unwrap();
}
$ cargo run
EXAMPLE 服务器启动,监听地址: http://localhost:8080
访问示例: http://localhost:8080/hello?name=Rust开发者

# 使用 curl 测试
$ curl http://localhost:8080/hello?name=Rust开发者
{"code":200,"message":"你好,Rust开发者!欢迎访问 Rust HTTP 服务","data":{"name":"Rust开发者"}}

$ curl http://localhost:8080/hello
{"code":200,"message":"你好,EXAMPLE!欢迎访问 Rust HTTP 服务","data":{"name":"EXAMPLE"}}

Axum is one of the most popular web frameworks in the Rust ecosystem, maintained by the Tokio team. It fully leverages Rust's type system to check operations such as route parameter extraction and state sharing at compile time, greatly reducing runtime errors.

Batch File Processing Tool

This is a CLI tool close to real-world scenarios: it recursively traverses directories, counts the number of lines and sizes of all files, and aggregates them by extension.

Add dependencies to Cargo.toml:

# Cargo.toml
[dependencies]
walkdir = "2"  # 递归遍历目录

Example

// File path: src/main.rs
use std::collections::HashMap;
use std::fs;
use walkdir::WalkDir; // Third-party library for recursively traversing directories

fn main() {
    let path = "."; // Scan starting from the current directory

    // Statistics: grouped by file extension
    // HashMap<extension, (number of files, total lines, total bytes)>
    let mut stats: HashMap<String, (usize, usize, u64)> = HashMap::new();

    // WalkDir recursively traverses directories, automatically skipping hidden files and the .git directory
    for entry in WalkDir::new(path)
        .into_iter()
        .filter_map(|e| e.ok()) // Filters out inaccessible entries
        .filter(|e| e.file_type().is_file()) // Only process files
    {
        // Get file extension
        let ext = entry
            .path()
            .extension()
            .and_then(|e| e.to_str())
            .unwrap_or("(no extension)")
            .to_lowercase();

        // Read file content (text files only; binary files may show garbled text but won't affect statistics)
        if let Ok(content) = fs::read_to_string(entry.path()) {
            let line_count = content.lines().count();
            let size = entry.metadata().map(|m| m.len()).unwrap_or(0);

            let (count, lines, total_size) = stats.entry(ext).or_insert((0, 0, 0));
            *count += 1;
            *lines += line_count;
            *total_size += size;
        }
    }

    // Sort by file count in descending order and output
    let mut sorted: Vec<_> = stats.into_iter().collect();
    sorted.sort_by(|a, b| b.1 .0.cmp(&a.1 .0));

    println!("=== EXAMPLE Directory File Statistics ===");
    println!("{:<15} {:>8} {:>10} {:>12}", "Extension", "File count", "Total lines", "Total size (bytes)");
    println!("{}", "-".repeat(50));
    for (ext, (count, lines, size)) in &sorted {
        println!("{:<15} {:>8} {:>10} {:>12}", ext, count, lines, size);
    }
}
$ cargo run --release
=== EXAMPLE 目录文件统计 ===
扩展名            文件数       总行数   总大小(bytes)
--------------------------------------------------
rs                  12        856        28432
toml                 3         45         1534
md                   2        120         3412
html                 1        510        14256
(无扩展名)            1          8          210

Common Tools and Command Reference

Besides Cargo's built-in features, the Rust ecosystem provides a series of tools to improve development efficiency:

CommandFunctionDescription
rustup updateUpdate the Rust toolchainUpgrade rustc, cargo, etc. to the latest stable version
rustup component add rustfmtInstall code formatting toolAutomatically format code on save
rustup component add clippyInstall code linting toolFind discouraged code patterns and potential bugs
rustup docOpen local documentationView the standard library and The Book offline
cargo add <crate>Add a dependencyAutomatically edit Cargo.toml and fetch the latest version
cargo updateUpdate dependency versionsUpdate Cargo.lock according to the version constraints in Cargo.toml
cargo treeView the dependency treeVisualize the project's complete dependency relationships
cargo benchRun benchmarksMeasure code performance
cargo install <tool>Install CLI tools written in Ruste.g., cargo install ripgrep

Notes and Common Issues

The following are the challenges Rust beginners most easily face and best practice recommendations.

Fighting with the Compiler Is Normal

Rust's learning curve is widely recognized as steep at first and gentler later.Ownership and borrow checkingare the biggest obstacle when starting out.

When the compiler repeatedly rejects the code you write, don't get discouraged — this precisely shows that the compiler is helping you catch potential runtime bugs. As you gain experience, you will gradually get used to thinking in Rust, and the compiler will become your most reliable partner rather than an enemy.

The Difference Between String and &str

This is the concept that Rust beginners most easily confuse.Stringis an owned, heap-allocated string (modifiable),&stris an immutable reference to string data (a borrowed view), usually derived from automatic dereferencing of &String.

Example

fn main() {
    // String: owns the data, modifiable, on the heap
    let mut s = String::from("Hello");

    // &str: reference, immutable, usually used as a function parameter
    let slice: &str = "EXAMPLE"; // String literals are &str themselves

    s.push_str(", world!"); // String can be modified
    println!("{}", s);
    println!("{}", slice);

    // Using &str (rather than String) for function parameters is more flexible
    greet("EXAMPLE");          // Can pass &str directly
    greet(&s);                // Can also be automatically converted from &String to &str
}

fn greet(name: &str) {
    println!("Hello, {}!", name);
}

Error Handling: panic! vs Result

Rust distinguishesunrecoverable errors(usingpanic!, the program terminates) andrecoverable errors(usingResult, the caller decides how to handle them).

In library code, you should return Result rather than panic!, giving the caller the choice. In applications, you can decide based on the specific situation.

unsafe: Use with Caution but Necessary

Rust provides theunsafekeyword, which allows low-level operations such as raw pointer manipulation and calling external functions (FFI).

unsafe does not mean "disabling safety checks"; rather, it makes developers responsible for the part of safety that the compiler cannot automatically guarantee. Most application code does not need to use unsafe — if you find yourself using it frequently, it may be a sign that your design needs adjustment.

Long Compile Times

Rust is known for long compile times, especially for large projects. This is because the compiler performs many checks (ownership analysis, lifetime validation, trait resolution, etc.) and optimizations at compile time.

Mitigation measures: usecargo checkinstead of cargo build for daily development; split large crates into sub-crates to take advantage of incremental compilation; usesccacheto cache compilation results; upgrade hardware (especially SSD and more CPU cores).

Learning Path Suggestions

For Rust beginners, the following learning order is recommended:

  • Read throughThe Rust Programming Language(the official book, free), focusing on the ownership chapters
  • Read and write code on the Rust Playground (play.rust-lang.org) to get immediate compiler feedback
  • ThroughRustlingsdo small exercises to reinforce the syntax
  • ReadRust by Example, learn through examples
  • Try rewriting a small project you're familiar with in Rust to understand Rust's design trade-offs in practice

Summary

With itsmemory safety (no GC)、zero-cost abstractionsandfearless concurrencythree core advantages, Rust occupies a unique niche in fields such as systems programming, high-performance services, WebAssembly, and blockchain.

Although the learning curve is steep, the compiler's strict checks translate into extremely high code quality and maintainability in long-term projects — which is why more and more companies and open-source projects choose Rust.

Rust's advantages and suitable use cases are summarized as follows:

DimensionRust's performance
Learning curveSteep (the ownership system requires understanding and adaptation), but the concepts are stable; once learned, you benefit for life
Compile speedRelatively slow (extensive compile-time checks), but mitigated by cargo check and incremental compilation
Runtime performanceSame level as C/C++, far surpassing languages with GC
Memory safetyGuaranteed at compile time, no GC needed, no null pointers, dangling pointers, or buffer overflows
Concurrency safetyEliminates data races at compile time, fearless concurrency
Ecosystem maturityGrowing rapidly, already very mature in systems programming, WebAssembly, and blockchain
Best suited forSystems programming, high-performance backends, WebAssembly, embedded systems, blockchain, CLI tools, databases
Not very suitable forRapid prototyping (compile time), GUI desktop applications, game development (compared to mature engines)
Other extensions