searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Rust的裸指针、引用和智能指针

2024-09-04 09:41:52
111
0

在 Rust 中,内存管理的核心概念包括裸指针(Raw Pointer)、引用(Reference)、和智能指针(Smart Pointer)。这些概念帮助 Rust 程序员以安全或灵活的方式处理内存。下面是它们的介绍及对比。


1. 裸指针(Raw Pointer)

定义

  • 裸指针是 Rust 中一种不受借用检查器管理的指针类型,通常用 *const T 表示不可变裸指针,用 *mut T 表示可变裸指针。

特点

  • 不安全:与 Rust 中的安全引用不同,裸指针不受借用检查器的保护,操作裸指针需要使用 unsafe 块,这意味着开发者需要自行确保内存安全。
  • 可以为 null:裸指针可以指向 null 或者任意的内存地址,这可能导致未定义行为(undefined behavior)。
  • 无需生命周期:由于不受 Rust 的借用规则约束,裸指针没有生命周期标注。

使用场景

  • 裸指针通常用于与底层操作系统、硬件交互或 FFI(Foreign Function Interface)时。使用时需要格外小心,确保指针指向有效的内存。

示例

fn main() {
    let mut num = 5;
    let r1 = &num as *const i32;  // 不可变裸指针
    let r2 = &mut num as *mut i32; // 可变裸指针

    unsafe {
        println!("r1 points to: {}", *r1);
        *r2 = 10;
        println!("r2 now points to: {}", *r2);
    }
}

2. 引用(Reference)

定义

  • 引用是 Rust 提供的一种安全指针类型,用于借用内存。不可变引用表示为 &T,可变引用表示为 &mut T

特点

  • 安全:引用受 Rust 的借用检查器保护,确保引用在使用时是有效的,并防止数据竞争。
  • 借用规则:Rust 通过借用规则保证内存安全:在任一时刻,对某一块数据要么只能有一个可变引用,要么只能有多个不可变引用。
  • 生命周期:引用有生命周期标注,编译器会根据生命周期规则推断引用的有效范围。

使用场景

  • 引用用于安全地访问和修改数据,不会夺取所有权,是 Rust 中最常用的指针类型。

示例

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1); // 借用 s1 的不可变引用
    println!("The length of '{}' is {}.", s1, len);
}

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

3. 智能指针(Smart Pointer)

定义

  • 智能指针是一类具有指针功能的结构体,不仅可以存储指向某个数据的指针,还可以提供额外的功能,如自动管理资源的生命周期。常见的智能指针包括 Box<T>Rc<T>Arc<T>RefCell<T> 等。

3.1 Box<T>

  • 特点

    • 将数据分配到堆上,并且提供对该数据的所有权。
    • Box<T>是单所有权类型,类似于 C++ 的 std::unique_ptr
  • 使用场景

    • 用于存储动态大小的数据类型或递归类型。
  • 示例

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

3.2 Rc<T> 和 Arc<T>

  • 特点

    • Rc<t>:用于单线程环境中的引用计数智能指针。允许多个所有者共享数据。
    • Arc<t>:类似 Rc<T>,但线程安全,用于多线程环境。
  • 使用场景

    • Rc<T>用于需要共享所有权但无需多线程的场景。
    • Arc<T>用于跨线程共享数据的场景。
  • 示例

    use std::rc::Rc;
    
    let a = Rc::new(5);
    let b = Rc::clone(&a);
    println!("count after creating b = {}", Rc::strong_count(&a));
    

3.3 RefCell<T>

  • 特点

    • 允许在运行时检查借用规则的智能指针。可以在不可变的上下文中进行可变借用,但只在单线程中有效。
  • 使用场景

    • 用于需要在运行时而非编译时检查借用规则的场景。
  • 示例

    use std::cell::RefCell;
    
    let x = RefCell::new(42);
    *x.borrow_mut() = 43;
    println!("x = {}", x.borrow());
    

对比总结

  1. 裸指针

    • 不安全,不受 Rust 借用检查器保护。
    • 通常用于与底层操作系统或硬件交互。
    • 无生命周期,不受 Rust 的所有权系统约束。
  2. 引用

    • 安全,有借用检查器和生命周期保护。
    • 最常用的指针类型,提供了安全的内存访问方式。
    • 受 Rust 的借用规则限制,防止数据竞争和悬垂引用。
  3. 智能指针

    • 提供额外功能的指针类型,如自动资源管理、引用计数、多线程安全等。
    • 常用于管理复杂内存场景,如堆分配、共享所有权和动态检查借用规则。

通过使用这些不同类型的指针,Rust 能够在提供内存安全的同时,满足各种复杂场景的需求。

0条评论
作者已关闭评论
YvetteChen
6文章数
0粉丝数
YvetteChen
6 文章 | 0 粉丝
原创

Rust的裸指针、引用和智能指针

2024-09-04 09:41:52
111
0

在 Rust 中,内存管理的核心概念包括裸指针(Raw Pointer)、引用(Reference)、和智能指针(Smart Pointer)。这些概念帮助 Rust 程序员以安全或灵活的方式处理内存。下面是它们的介绍及对比。


1. 裸指针(Raw Pointer)

定义

  • 裸指针是 Rust 中一种不受借用检查器管理的指针类型,通常用 *const T 表示不可变裸指针,用 *mut T 表示可变裸指针。

特点

  • 不安全:与 Rust 中的安全引用不同,裸指针不受借用检查器的保护,操作裸指针需要使用 unsafe 块,这意味着开发者需要自行确保内存安全。
  • 可以为 null:裸指针可以指向 null 或者任意的内存地址,这可能导致未定义行为(undefined behavior)。
  • 无需生命周期:由于不受 Rust 的借用规则约束,裸指针没有生命周期标注。

使用场景

  • 裸指针通常用于与底层操作系统、硬件交互或 FFI(Foreign Function Interface)时。使用时需要格外小心,确保指针指向有效的内存。

示例

fn main() {
    let mut num = 5;
    let r1 = &num as *const i32;  // 不可变裸指针
    let r2 = &mut num as *mut i32; // 可变裸指针

    unsafe {
        println!("r1 points to: {}", *r1);
        *r2 = 10;
        println!("r2 now points to: {}", *r2);
    }
}

2. 引用(Reference)

定义

  • 引用是 Rust 提供的一种安全指针类型,用于借用内存。不可变引用表示为 &T,可变引用表示为 &mut T

特点

  • 安全:引用受 Rust 的借用检查器保护,确保引用在使用时是有效的,并防止数据竞争。
  • 借用规则:Rust 通过借用规则保证内存安全:在任一时刻,对某一块数据要么只能有一个可变引用,要么只能有多个不可变引用。
  • 生命周期:引用有生命周期标注,编译器会根据生命周期规则推断引用的有效范围。

使用场景

  • 引用用于安全地访问和修改数据,不会夺取所有权,是 Rust 中最常用的指针类型。

示例

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1); // 借用 s1 的不可变引用
    println!("The length of '{}' is {}.", s1, len);
}

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

3. 智能指针(Smart Pointer)

定义

  • 智能指针是一类具有指针功能的结构体,不仅可以存储指向某个数据的指针,还可以提供额外的功能,如自动管理资源的生命周期。常见的智能指针包括 Box<T>Rc<T>Arc<T>RefCell<T> 等。

3.1 Box<T>

  • 特点

    • 将数据分配到堆上,并且提供对该数据的所有权。
    • Box<T>是单所有权类型,类似于 C++ 的 std::unique_ptr
  • 使用场景

    • 用于存储动态大小的数据类型或递归类型。
  • 示例

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

3.2 Rc<T> 和 Arc<T>

  • 特点

    • Rc<t>:用于单线程环境中的引用计数智能指针。允许多个所有者共享数据。
    • Arc<t>:类似 Rc<T>,但线程安全,用于多线程环境。
  • 使用场景

    • Rc<T>用于需要共享所有权但无需多线程的场景。
    • Arc<T>用于跨线程共享数据的场景。
  • 示例

    use std::rc::Rc;
    
    let a = Rc::new(5);
    let b = Rc::clone(&a);
    println!("count after creating b = {}", Rc::strong_count(&a));
    

3.3 RefCell<T>

  • 特点

    • 允许在运行时检查借用规则的智能指针。可以在不可变的上下文中进行可变借用,但只在单线程中有效。
  • 使用场景

    • 用于需要在运行时而非编译时检查借用规则的场景。
  • 示例

    use std::cell::RefCell;
    
    let x = RefCell::new(42);
    *x.borrow_mut() = 43;
    println!("x = {}", x.borrow());
    

对比总结

  1. 裸指针

    • 不安全,不受 Rust 借用检查器保护。
    • 通常用于与底层操作系统或硬件交互。
    • 无生命周期,不受 Rust 的所有权系统约束。
  2. 引用

    • 安全,有借用检查器和生命周期保护。
    • 最常用的指针类型,提供了安全的内存访问方式。
    • 受 Rust 的借用规则限制,防止数据竞争和悬垂引用。
  3. 智能指针

    • 提供额外功能的指针类型,如自动资源管理、引用计数、多线程安全等。
    • 常用于管理复杂内存场景,如堆分配、共享所有权和动态检查借用规则。

通过使用这些不同类型的指针,Rust 能够在提供内存安全的同时,满足各种复杂场景的需求。

文章来自个人专栏
文章 | 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0