Rust编程入门:所有权与借用

大家好,我是欧阳瑞(Rich Own)。今天想和大家聊聊Rust编程语言。作为一个Web3探索者,我最近一直在学习Rust,因为它是Solana等区块链平台的主要开发语言。今天就来分享一下Rust最核心的概念——所有权与借用。

为什么选择Rust?

Rust是一门系统级编程语言,具有以下特点:

特性 说明
内存安全 编译时保证内存安全,无需垃圾回收
高性能 接近C/C++的性能
并发安全 编译时检测数据竞争
现代语法 类似现代高级语言的语法

所有权规则

Rust的所有权系统是其最独特的特性,有三条规则:

  1. 每个值都有一个所有者
  2. 值在任意时刻只能有一个所有者
  3. 当所有者离开作用域,值将被丢弃

基础示例

fn main() {
    let s1 = String::from("hello");
    let s2 = s1; // s1的所有权移动到s2
    
    // println!("{}", s1); // 错误!s1不再有效
    println!("{}", s2); // 正确
}

栈数据与堆数据

fn main() {
    // 栈数据:Copy语义
    let x = 5;
    let y = x; // x被复制,x仍然有效
    println!("x = {}, y = {}", x, y); // 正确
    
    // 堆数据:Move语义
    let s1 = String::from("hello");
    let s2 = s1; // s1的所有权移动到s2
    // println!("s1 = {}", s1); // 错误!
    println!("s2 = {}", s2); // 正确
}

借用

什么是借用?

借用是指引用某个值而不获取其所有权。

fn main() {
    let s = String::from("hello");
    let len = calculate_length(&s); // 借用s
    println!("Length of '{}' is {}", s, len); // s仍然有效
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

可变借用

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

fn change(s: &mut String) {
    s.push_str(", world");
}

借用规则

  1. 同一时刻只能有一个可变引用
  2. 可变引用和不可变引用不能同时存在
  3. 引用必须始终有效
fn main() {
    let mut s = String::from("hello");
    
    let r1 = &mut s;
    // let r2 = &mut s; // 错误!同一时刻只能有一个可变引用
    
    println!("{}", r1);
}

生命周期

什么是生命周期?

生命周期确保引用在其指向的数据有效期间保持有效。

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 string2 = String::from("xyz");
    
    let result = longest(string1.as_str(), string2.as_str());
    println!("The longest string is {}", result);
}

结构体中的生命周期

struct ImportantExcerpt<'a> {
    part: &'a str,
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("Could not find a '.'");
    let i = ImportantExcerpt { part: first_sentence };
}

静态生命周期

fn main() {
    let s: &'static str = "I have a static lifetime";
    println!("{}", s);
}

智能指针

Box

fn main() {
    let b = Box::new(5);
    println!("b contains: {}", b);
}

Rc - 引用计数指针

use std::rc::Rc;

fn main() {
    let a = Rc::new(String::from("hello"));
    let b = Rc::clone(&a);
    let c = Rc::clone(&a);
    
    println!("a = {}, b = {}, c = {}", a, b, c);
    println!("count = {}", Rc::strong_count(&a)); // 3
}

RefCell - 内部可变性

use std::cell::RefCell;

fn main() {
    let x = RefCell::new(5);
    
    *x.borrow_mut() += 1;
    println!("x = {}", x.borrow()); // 6
}

实战:实现一个简单的链表

use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug)]
struct Node<T> {
    value: T,
    next: Option<Rc<RefCell<Node<T>>>>,
}

#[derive(Debug)]
struct LinkedList<T> {
    head: Option<Rc<RefCell<Node<T>>>>,
}

impl<T> LinkedList<T> {
    fn new() -> Self {
        LinkedList { head: None }
    }
    
    fn push(&mut self, value: T) {
        let new_node = Rc::new(RefCell::new(Node {
            value,
            next: self.head.take(),
        }));
        
        self.head = Some(new_node);
    }
    
    fn pop(&mut self) -> Option<T> {
        self.head.take().map(|node| {
            let node = Rc::try_unwrap(node).ok().unwrap();
            self.head = node.borrow().next.clone();
            node.into_inner().value
        })
    }
}

fn main() {
    let mut list = LinkedList::new();
    list.push(1);
    list.push(2);
    list.push(3);
    
    println!("{:?}", list); // LinkedList { head: Some(RefCell { value: Node { value: 3, next: Some(...) } }) }
    
    println!("{}", list.pop().unwrap()); // 3
    println!("{}", list.pop().unwrap()); // 2
    println!("{}", list.pop().unwrap()); // 1
}

错误处理

Result类型

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

fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err(String::from("Cannot divide by zero"))
    } else {
        Ok(a / b)
    }
}

fn main() {
    match divide(10.0, 2.0) {
        Ok(result) => println!("Result: {}", result),
        Err(e) => println!("Error: {}", e),
    }
}

?操作符

use std::fs::File;
use std::io::{self, Read};

fn read_file_contents(path: &str) -> Result<String, io::Error> {
    let mut file = File::open(path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

总结

Rust的所有权系统是其最核心的特性,虽然学习曲线较陡,但一旦掌握,就能写出既安全又高效的代码。所有权、借用和生命周期是理解Rust的关键。

我的鬃狮蜥Hash对Rust也有自己的理解——它总是小心地管理自己的"所有权",确保每一只蟋蟀都被妥善处理。这也许就是自然界的"内存安全"吧!

如果你对Rust编程感兴趣,欢迎留言交流!我是欧阳瑞,极客之路,永无止境!


技术栈:Rust · 所有权 · 借用 · 生命周期

更多推荐