什么时候将特征标记为不安全,而不是将特征中的所有函数标记为不安全?

When is it appropriate to mark a trait as unsafe, as opposed to marking all the functions in the trait as unsafe?

在代码中说同样的话,我什么时候会选择以下任一示例?

unsafe trait MyCoolTrait {
    fn method(&self) -> u8;
}

trait MyCoolTrait {
    unsafe fn method(&self) -> u8;
}

opt-in builtin traits (OIBIT) RFC 状态:

An unsafe trait is a trait that is unsafe to implement, because it represents some kind of trusted assertion. Note that unsafe traits are perfectly safe to use. Send and Share (note: now called Sync) are examples of unsafe traits: implementing these traits is effectively an assertion that your type is safe for threading.

标准库中还有另一个不安全特征的例子,Searcher。它说:

The trait is marked unsafe because the indices returned by the next() methods are required to lie on valid utf8 boundaries in the haystack. This enables consumers of this trait to slice the haystack without additional runtime checks.

不幸的是,这两段都没有真正帮助我理解何时将整个特征标记为不安全而不是部分或所有方法是正确的。

之前的,但是这次好像不一样了。

一个函数被标记为 unsafe 表示调用它可能会违反内存安全。特征被标记为 unsafe 以指示通过实现它 完全 可能会违反内存安全。这通常是因为特征具有其他不安全代码所依赖的不变量,并且这些不变量不能以任何其他方式表达。

Searcher 的情况下,方法本身 应该 可以安全调用。也就是说,用户 不必担心他们是否正确使用 Searcher;接口契约说所有调用都是安全的。您无能为力会导致这些方法违反内存安全。

但是,不安全的代码会调用Searcher的方法,这样的不安全代码会依赖给定的 Searcher 实现 return 偏移量位于有效的 UTF-8 代码点边界上。如果违反此假设,则不安全代码最终可能会导致内存安全违规。

换句话说:使用 Searchers 的不安全代码的正确性取决于 每个 Searcher 实现 正确。或者:错误地实现此特征允许安全代码 引发 内存安全违规是不相关的 unsafe 代码。

那么为什么不标记方法 unsafe?因为它们 一点也不 不安全!他们不会任何可能违反自身内存安全的事情。 next_match 只是扫描 return 一个 Option<(usize, usize)>。仅当不安全代码 假定 这些 usize 是正在搜索的字符串中的有效索引时才存在危险。

那么为什么不直接检查结果呢?因为那会更慢。搜索代码想要 fast,这意味着它想要避免冗余检查。但是这些检查不能在 Searcher 接口中表达...因此,整个特征被标记为 unsafe 以警告任何实施它的人有额外的条件 not 在代码中声明或强制执行 必须 得到遵守。

还有 SendSync:在您不应该违反(除其他事项外)必须处理线程的代码的期望时实施它们。允许您创建线程的代码是安全的,但是 only 只要 SendSynconly 实现的它们适合的类型。

经验法则是这样的:

  • 如果方法用户需要将方法调用包装在 unsafe 块中,请使用 unsafe fn method()

  • 如果特征实现者需要unsafe impl MyTrait,请使用unsafe trait MyTrait

unsafe 是给 Rust 用户的提示:必须小心编写不安全的代码。 关键是 unsafe 应该作为双重使用:当作者声明 trait/function 不安全时,implementor/user 需要 implement/use 它与 unsafe

当功能标记为unsafe时,表示用户需要谨慎使用该功能。功能作者假设功能用户必须保留。

当trait被标记为unsafe时,表示trait实现者需要谨慎实现。该特征要求实现者保持一定的假设。但是不安全特性的用户可以漫不经心地调用特性中定义的方法。

对于具体的例子,unsafe trait Searcher 要求所有 Searcher 实现在调用 next 时应该 return 有效的 utf8 边界。并且所有实现都标记为unsafe impl Searcher,表明实现代码可能不安全。但是作为 Searcher 的用户,可以调用 searcher.next() 而无需将其包装在 unsafe 块中。