大家好,我是梦兽编程。没错Rust系列将以谷歌安卓团队分享的Rust经验进行持续更新。

这是由 Google 的 Android 团队开发的免费 Rust 课程。本课程涵盖了 Rust 的方方面面,从基本语法到泛型和错误处理等高级主题。

该课程的最新版本可以在 https://google.github.io/comprehensive-rust/找到。如果您正在其他地方阅读,请检查那里的更新。

Rust是如何保证安全的?

相信大家听闻Rust都是以它的安全性宣传,那问题来了Rust是如何保证的安全性?Rust的编译器在给我们做了以下的操作。

Rust编译器做了很多限制,在编译时对静态内存做了一下处理:

没有未初始化的变量

Rust 的所有变量都必须在使用前进行初始化。这意味着在使用变量之前,必须将其分配一个值。Rust 编译器会在编译时检查所有变量是否已被初始化,如果未初始化,则会报错。比如以下情况:

fn main() {
    let x:i32;
    println!("x -> {}",x)
}

Java和Golang这一类的语言初始变量时候已经默认给我们带上了默认值。如果不给默认值的话估计你会遇到类似(javascript)这种undefined的情况,可能导致你的系统出现问题。

没有内存泄漏

Rust 使用所有权系统来管理内存。所有权系统将所有内存分配与其所有者联系起来。当所有者离开作用域时,其所拥有的内存将被自动释放。

初学者会对Rust的内存权限很难掌握,但没特别是借用引用这些关系应用在生命周期当中。

这意味着在大多数情况下,Rust 程序不会发生内存泄漏。但是,在以下情况下,Rust 程序可能会发生内存泄漏:

  • 使用 Rc 或 Weak 智能指针。
  • 使用 Box 智能指针。
  • 使用 unsafe 代码。 通常在引用一些C代码块的时候需要用上unsafe来绕过编译器的检查

没有双重释放

Rust 编译器会在编译时检查所有内存是否已被释放两次。如果一个内存块已被释放两次,则会报错。

fn main(){
    let mut ptr = Box::new(5);
    // 释放内存
    ptr.drop();
    // 再次释放内存 Error
    ptr.drop();
}

释放后不使用

Rust 编译器会在编译时检查所有已释放的内存是否仍然被使用。如果一个已释放的内存块仍然被使用,则会报错。

fn main() {
    let mut ptr = Box::new(5);

    // 释放内存
    ptr.drop();

    // 尝试使用已释放的内存
    // 这将导致错误
    println!("{}", *ptr);
}

没有NULL指向

Rust 的所有指针都必须指向有效的内存。如果一个指针指向无效的内存,则会导致程序崩溃。Rust 编译器会在编译时检查所有指针是否指向有效的内存,如果一个指针指向无效的内存,则会报错。

fn main() {
    let ptr: *mut i32 = null;

    // 尝试使用 `ptr`
    // 这将导致错误
    println!("{}", *ptr);
}

没有忘记锁的释放

在并发开发中,线程和线程之间我们往往需要使用锁来保证线程与线程之间的资源争夺,使用锁。往往锁使用不好就很容易形成死锁,特别有些新手在java语言或者golang这一类语法中,使用完锁却忘记释放,导致下一次的访问,其他线程都在等待锁的释放进行下一轮的资源争夺。

在Rust中是拒绝这种情况发生的。

fn main() {
    let mut mtx = Mutex::new(0);

    // 尝试获取互斥锁
    // 这将成功
    let mut guard = mtx.lock();

    // 做一些事情

    // 忘记解锁互斥锁
}

正确的做法。

fn main() {
    let mut mtx = Mutex::new(0);

    // 尝试获取互斥锁
    // 这将成功
    let mut guard = mtx.lock();

    // 做一些事情

    // 正确解锁互斥锁
    guard.unlock();
}

没有迭代器的实效

如果你有时候需要手写一个迭代器的时候,在java或golang这些运行时的语言中,可能会出现以下写法问题,梦兽编程这里以Java举例子,因为golang没有自带迭代器,golang团队推荐使用for。

public class IteratorTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3);
        Iterator<Integer> iter = list.iterator();

        // 迭代器的范围结束
        iter.next();
        iter.next();
        iter.next();

        // 尝试使用已超出范围的迭代器
        // 这将导致程序崩溃
        System.out.println(iter.next());
    }
}

这里的代码JAVA代码是可以编译成功的,只有运行时才会保存,直接导致你的程序崩溃,不得不去重启服务。

以上的问题在Rust中编译器是服务让你编译成功的。

fn main() {
    let mut iter = vec![1, 2, 3].into_iter();

    // 迭代器的范围结束
    iter.next();
    iter.next();
    iter.next();

    // 尝试使用已超出范围的迭代器
    // 这将导致错误
    println!("{}", iter.next().unwrap());
}

output Info: 编译很贴心的告诉你,你超出访问了不应该写三个next() ^_^

error[E0502]: cannot borrow `iter` as mutable, as it is not declared as mutable
  --> src/main.rs:10:5
   |
9 |     iter.next();
   |     iter.next();
   |     iter.next();
   |     ^ value used here after move

好了以上就是本次的所有内容,希望能为你解答出Rust是如何保证程序的运行安全有所理解,这里是梦兽编程-本次系列是根本《谷歌安卓团队学习Rust》。

https://rexweb.link/category/technical-column/learn-rust-from-the-android-team或者更多技术可以收藏梦兽编程https://rexweb.link

  1. 梦兽编程倔强号
  2. 梦兽编程知乎
  3. 梦兽编程bilibili

微信搜索梦兽编程公众号

梦兽编程倔强号

https://juejin.cn/user/2066737588876983

梦兽编程知乎

https://www.zhihu.com/people/mo-dong-74

梦兽编程bilibili

https://space.bilibili.com/106325238?spm_id_from=333.1007.0.0


梦兽编程
1 声望0 粉丝