何时将特征标记为不安全,而不是将特征中的所有功能标记为不安全?

问题描述 投票:10回答:2

在代码中说同样的事情,我什么时候会选择以下任何一个例子?

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

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

opt-in builtin traits (OIBIT) RFC说:

不安全的特征是一种实现不安全的特性,因为它代表了某种可信的断言。请注意,不安全的特性使用起来非常安全。 SendShare(注意:现在称为Sync)是不安全特征的例子:实现这些特征实际上是一种断言,你的类型对于线程是安全的。

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

该特征被标记为不安全,因为next()方法返回的索引需要位于大海捞针中有效的utf8边界。这使得该特征的消费者能够在没有额外运行时检查的情况下切割干草堆。

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

I've asked about marking a function as unsafe之前,但这似乎不同。

rust traits
2个回答
13
投票

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

Searcher的情况下,这些方法本身应该是安全的。也就是说,用户不必担心他们是否正确使用Searcher;接口合同说所有电话都是安全的。你无能为力会导致这些方法违反记忆安全。

但是,不安全的代码将调用Searcher的方法,并且这种不安全的代码将依赖于给定的Searcher实现来返回有效的UTF-8代码点边界上的偏移量。如果违反了这个假设,那么不安全的代码最终可能会导致内存安全违规。

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

那么为什么不标记方法unsafe?因为它们根本不是不安全的!他们不做任何可能违反记忆安全的事情。 next_match只是扫描并返回Option<(usize, usize)>。只有当不安全的代码假定这些usizes是被搜索字符串的有效索引时,才存在这种危险。

那么为什么不检查结果呢?因为那会慢一些。搜索代码想要快速,这意味着它希望避免冗余检查。但是这些检查不能在Searcher界面中表达...所以相反,整个特征被标记为unsafe,以警告任何实现它的人,在必须遵守的代码中没有陈述或强制执行的额外条件。

还有SendSync:当你不应该违反(必要时)必须处理线程的代码的期望时实现这些。允许您创建线程的代码是安全的,但只要SendSync只在它们适合的类型上实现。


0
投票

经验法则是这样的:

  • 如果方法用户需要在unsafe fn method()块中包装方法调用,请使用unsafe
  • 如果特质实现者需要unsafe trait MyTrait,请使用unsafe impl MyTrait

unsafe是Rust用户的提示:必须仔细编写不安全的代码。关键点是unsafe应该用作双重:当作者声明特征/函数不安全时,实现者/用户需要使用unsafe来实现/使用它。

当函数标记为unsafe时,表示用户需要仔细使用该函数。函数作者假设用户必须保留该函数。

当特征被标记为unsafe时,这意味着特征实现者需要仔细实施。特征要求实施者保持一定的假设。但是,不安全特征的用户可能会无意中调用特征中定义的方法。

举个具体例子,unsafe trait Searcher要求所有Searcher实现在调用next时应返回有效的utf8边界。并且所有实现都标记为unsafe impl Searcher,表示实现代码可能不安全。但作为Searcher的用户,可以调用searcher.next()而不将其包裹在unsafe块中。

© www.soinside.com 2019 - 2024. All rights reserved.