为什么 Rust 标准库有这么多不安全代码?

Nig*_*eXD 3 unsafe rust

我在标准库中查看了String很多unsafe这样的代码:

    #[inline]
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn remove(&mut self, idx: usize) -> char {
        let ch = match self[idx..].chars().next() {
            Some(ch) => ch,
            None => panic!("cannot remove a char from the end of a string"),
        };

        let next = idx + ch.len_utf8();
        let len = self.len();
        unsafe {
            ptr::copy(self.vec.as_ptr().add(next), self.vec.as_mut_ptr().add(idx), len - next);
            self.vec.set_len(len - (next - idx));
        }
        ch
    }
Run Code Online (Sandbox Code Playgroud)

unsafe为什么标准库里有这么多代码?语言如何仍然安全?

kmd*_*eko 15

这里有一个误解,认为使用unsafe自然是不健全的,并且会导致内存错误。它不是。unsafe事实上,即使是在代码块中,也不允许出现内存错误;如果这样做,那么代码将表现出未定义的行为,并且整个程序定义不明确。重点unsafe是允许编译器无法确保实际上安全的事情。unsafe开发人员有责任通过了解使用语法、函数和其他项目所需的安全要求来确保代码不会调用未定义的行为。

编写和使用函数的设计理念unsafe是,如果某些参数或情况可能导致函数表现出未定义的行为,则必须对其进行标记unsafe并记录安全参数和情况。然后,调用者必须在块内遵守此文档unsafe。这种设计理念的另一面是,如果一个函数没有标记unsafe,那么任何可能的参数或情况都不会导致未定义的行为。

在这种情况下,在内存中移动字节并不总是安全的,因此您必须使用unsafeto call ptr::copy。然而,该方法.remove()没有被标记unsafe,因此如果 Rust 标准库的开发人员已经完成了他们的工作,那么块中发生的任何事情都unsafe必须是安全的,而且我确信他们已经做到了。您可以看到任何可能的输入都经过了边界检查,并且正在处理的内容copy位于已分配的块内。可能导致未定义行为的唯一方法是在调用此函数之前已经存在未定义行为或损坏的不变量。


如果不使用unsafe. 计算机所基于的底层手动内存管理本质上充满了内存枪,但是您可以通过保证这些“不安全”操作的安全来构建它们。

其中一些unsafe是必需的,但其他实例只是出于性能原因。安全抽象可能需要许多检查来确保它们是安全的,特别是如果涉及任何类型的动态性,但如果现有的不变量编码正确,那么使用unsafe可以避免这些检查,同时仍然安全。在此函数中,只需依赖其他方法(在某些时候内部会这样做),它可能就可以完全安全地完成,但它可能包括完全不必要的额外边界检查。self.vecunsafe

标准库期望以尽可能少的开销运行,同时保持安全(当然除非该函数被标记unsafe)。