Rust中调用不安全方法或函数的方法

132 阅读3分钟

这篇博客中,我们将看到不安全的Rust的另一个特点。Rust有很好的借用和所有权规则,可以照顾到所有的内存安全问题。它是领先的语言之一。超过 9家 公司,包括Dropbox、Coursera、Figma、npm、微软、Cloudflare、Facebook、Amazon和Discord,都在使用Rust做这样或那样的事情。但有时Rust不让我们编译程序,即使我们知道该程序可以安全执行。在这种情况下,我们使用不安全的Rust。所以,让我们开始看看不安全Rust的另一个特点。

调用不安全的方法或函数

我们使用一个不安全块来调用一个 不安全的方法函数。一个不安全的方法函数和普通的函数是一样的,只是在它的定义前有一个不安全的关键字 。它表明它有一些要求要满足,但Rust不能保证这些要求已经得到满足。所以当我们定义一个不安全的函数时,我们需要确保该函数的要求得到满足。

要调用一个不安全的方法或函数,我们只需在一个不安全块内调用该函数,以告诉Rust编译器下面调用的是一个不安全的函数。

例如,请看下面的代码。

unsafe fn demo(){}

fn main()
{
    demo();
}

这段代码调用了一个不安全的函数,但没有一个不安全块。所以我们得到了下面的错误。

我们只需在一个不安全块内调用demo 函数,就可以消除上述错误,如下图所示。通过在调用dangerous 的周围插入unsafe 块,我们向 Rust 断言,我们已经阅读了该函数的文档,我们知道如何正确使用它。

unsafe fn demo(){}

fn main()
{
    unsafe{
        demo();
    }
}

在不安全的代码上创建一个安全的抽象

如果一个函数只包含一些不安全的代码,我们不需要把整个函数标记为不安全。相反,我们可以 代码 包裹 在安全函数内的不安全块 中。这是一种定义不安全代码的常见抽象技术。

请看一下下面的代码,以便更好地理解它。

fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
   
    let len = slice.len();

    assert!(mid <= len);

    (&mut slice[..mid], &mut slice[mid..])
}

fn main(){

    let mut vec_to_split = vec![1, 2, 3, 4, 5, 6];

    let ref_to_vec = &mut vec_to_split[..];

    let (part_1, part_2) = split_at_mut(ref_to_vec,3);

    assert_eq!(part_1, &mut [1, 2, 3]);
    assert_eq!(part_2, &mut [4, 5, 6]);
}

上面的代码什么也没做,只是定义了一个函数split_at_mut ,该函数在i32上获取一个可变片 ,并在第二个参数给出的 索引处将其分割成两个。这段代码在编译时产生了一个错误。这个错误在下面给出。

该错误显示,该函数使用了对同一分片的 两个易变的引用。但是我们所做的是完全安全的,因为我们借用的是分片的不同部分 ,这很好。Rust 编译器不知道这一点,所以不允许我们编译以避免任何问题。既然我们知道这段代码没有问题,不会引起任何问题,我们就知道该怎么做。

为了避免错误并使程序运行,我们对我们的代码做了如下修改。

use std::slice;

fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
   
    let len = slice.len();

    let ptr = slice.as_mut_ptr();

    assert!(mid <= len);

    unsafe {
        (
            slice::from_raw_parts_mut(ptr, mid),
            slice::from_raw_parts_mut(ptr.add(mid), len - mid),
        )
    }
    
}

fn main(){

    let mut vec_to_split = vec![1, 2, 3, 4, 5, 6];

    let ref_to_vec = &mut vec_to_split[..];

    let (part_1, part_2) = split_at_mut(ref_to_vec,3);

    assert_eq!(part_1, &mut [1, 2, 3]);
    assert_eq!(part_2, &mut [4, 5, 6]);
}

我们使用一些不安全的函数来避免错误,然后使用一个不安全的块,这样我们就不必让整个函数变得不安全。这是对不安全代码的一种安全抽象。我们使用from_raw_parts_mut 函数,该函数将原始指针长度作为参数,然后切割从指针开始的部分片断。

add 函数也是不安全的,因为它把mid 作为参数,并返回一个从mid 开始的原始指针。因此,它必须相信偏移的位置也是一个有效的指针。

所以这就是关于调用不安全的方法或函数的全部内容。