< 返回版块

rock117 发表于 2022-09-19 22:05

Rust死灵书中有个例子

struct Vec { data: *const T, // *const 是可变异的! len: usize, cap: usize, }

关于这个例子存在不健壮性,解释说明是这样的: https://nomicon.purewhite.io/phantom-data.html

丢弃检查器将慷慨地确定Vec不拥有任何 T 类型的值。这将反过来使它得出结论,它不需要担心 Vec 在其析构器中丢弃任何 T 来确定丢弃检查的合理性。这将反过来允许人们使用 Vec 的析构器来制造不健壮性。

可否有大神举个例子,上面的代码如何在Vec的析构器中制造不健壮性?

评论区

写评论
作者 rock117 2022-09-20 22:14

刚看了下英文,感觉有些理解了, 中文居然漏了很多东西

--
👇
苦瓜小仔:

我十分建议你重新看英文原文,因为

一些词语翻译得并不准确

如果你已经知道这里翻译的“变异”不是“可变”而是“子类型的组织规则”,忽略下述内容。

variance 应为“型变”而不是“变异”,当然重要的不是中文,而是它描述的东西 —— 这在这本书中有专门的一篇。首先要理解“子类型”,然后是“型变”(我从那章摘抄的关键):

Nomicon 一书这样描述和总结了 Rust 中的型变:

是一组子类型应该如何组织起来的规则
定义了禁用子类型的情况
是类型构造器相对于其参数所具有的属性
类型构造器 F 的型变,就是指其输入的子类型如何影响其输出的子类型
在实际场景中,型变 (variance) 这一术语(很多情况下)指“协变” (covariance)
几乎所有对型变的考虑都是关于某事应该是协变的还是不变的
在 Rust 中见证逆变是相当困难的,尽管它实际上的确存在

子类型/型变主要发生在生命周期上。

而“变异”让人联想起“可变性”(mutability,就是变量的改变)。

阅读翻译的方式应该是中文为辅、英文为主

Rust 真正的官方材料就那么些,如果你真的想知道这些细节,至少要明白每句话大概在说什么,尤其是原文在说什么。只看中文是远远不够的。对我来说,像什么“丢弃”啊、“幽灵数据”啊,是理解障碍,增加记忆负担,人家就叫 drop/Drop 和 PhantomData —— 你会把 Vec 翻译成“向量”(vector) 去理解吗?我独自翻译的 Rust 开源书籍少说也有四本了,所以作为翻译者能理解有时候避免不了为了中文表词达意,把自己觉得很好理解的东西在专有名词上翻译地蹩脚。


可以回答你的问题了:

这篇翻译遗漏了一些内容(可能因为作者问题,也可能因为原文更新问题):

  1. 没有翻译原文折叠的部分,那里描述了你想知道的“不健壮性”的具体例子,但主要讲了 #[may_dangle] 的用法。

  2. struct Vec { data: *const T, len: usize, cap: usize, } 这个例子还有后半部分:

But ever since RFC 1238, this (add an extra PhantomData) is no longer true nor necessary. Adding an extra _owns_T: PhantomData field is thus superfluous and accomplishes nothing.

所以,这是一个不需要 PhantomData 的反例!

我总结一下这两部分吧:在手动实现 Drop 中,PhantomData 不再必要,因为它对 drop 没有影响,反而可能会考虑 #[may_dangle]。

然后综合全文:PhantomData 的一个重要应用是描述型变。

苦瓜小仔 2022-09-20 14:42

@eweca-d 你的时间线不太对。

那部分内容与 drop check 有关,而且基于标准库中的 Vec<T> 来举例,重新整理一下应该是:

过去在 Vec<T>

  1. 使用 PhantomData<T> 的原因是让 drop checker 知道在 drop Vec<T> 时应该 drop T
  2. 不使用 PhantomData<T> 会导致 drop check unsoundness

现在考虑 drop check 时无需使用 PhantomData<T>

这是由 RFC 1238 带来的。

因为现在 impl<T> Drop for Vec<T> 会考虑在 Drop 实现中考虑 drop T

实际上现在的 Vec<T> 使用了 PhantomData<T>#[may_dangle]

#[may_dangle] 需要 unsafe impl,目的是允许

v.push(&s);
drop(s);
// &s 在这里悬空
} // 块末尾 drop Vec,但不考虑悬空的 &s

显然, #[may_dangle] 过于宽松(excessively loose),因为我们真正想要的是:

"T may dangle provided it not be involved in some transitive drop glue". This "exception to the exception" is a pervasive situation whenever we own a T. That's why Rust's #[may_dangle] is smart enough to know of this opt-out, and will thus be disabled when the generic parameter is held in an owned fashion by the fields of the struct.

  1. 在不涉及直接 drop 的胶水代码里,允许 dangle,比如 impl<'s> Drop for Vec<&'s str> 中让 drop(s) 先于 (隐式的?)drop(v)
  2. 在涉及直接 drop 的情况中,drop Vec 应考虑 drop T,比如 Drop for Vec<PrintOnDrop<'s>>

drop check 水很深(记录的文档是不是比生命周期还多???),我平时用不到,知道些基本的规则,没怎么关注过。

试了下很复杂,碰上 NLL 未能解决的问题。Hmm。。。

eweca-d 2022-09-20 10:42

应该是更新问题,我看了下自己的nomicon,没有这部分内容,然后更新了一下(从7月的版本到8月8的版本),这个新内容出现了。

所以看起来是在原先设计情况下,假如T有一个生命周期&'a的情况下,能够保证T超过生命周期被Drop之后,Vec<T>也能被Drop?从而避免使用者尝试使用里面的指针(此时是悬垂指针)?

新的may_dangle啥意思?nomicon说过去的方法太过于严格,意思是在Vec<T>不含真正含有T的时候,当T超过生命周期被Drop,这种情况下不再Drop Vec<T>

苦瓜小仔 2022-09-20 01:24

我十分建议你重新看英文原文,因为

一些词语翻译得并不准确

如果你已经知道这里翻译的“变异”不是“可变”而是“子类型的组织规则”,忽略下述内容。

variance 应为“型变”而不是“变异”,当然重要的不是中文,而是它描述的东西 —— 这在这本书中有专门的一篇。首先要理解“子类型”,然后是“型变”(我从那章摘抄的关键):

Nomicon 一书这样描述和总结了 Rust 中的型变:

是一组子类型应该如何组织起来的规则
定义了禁用子类型的情况
是类型构造器相对于其参数所具有的属性
类型构造器 F 的型变,就是指其输入的子类型如何影响其输出的子类型
在实际场景中,型变 (variance) 这一术语(很多情况下)指“协变” (covariance)
几乎所有对型变的考虑都是关于某事应该是协变的还是不变的
在 Rust 中见证逆变是相当困难的,尽管它实际上的确存在

子类型/型变主要发生在生命周期上。

而“变异”让人联想起“可变性”(mutability,就是变量的改变)。

阅读翻译的方式应该是中文为辅、英文为主

Rust 真正的官方材料就那么些,如果你真的想知道这些细节,至少要明白每句话大概在说什么,尤其是原文在说什么。只看中文是远远不够的。对我来说,像什么“丢弃”啊、“幽灵数据”啊,是理解障碍,增加记忆负担,人家就叫 drop/Drop 和 PhantomData —— 你会把 Vec 翻译成“向量”(vector) 去理解吗?我独自翻译的 Rust 开源书籍少说也有四本了,所以作为翻译者能理解有时候避免不了为了中文表词达意,把自己觉得很好理解的东西在专有名词上翻译地蹩脚。


可以回答你的问题了:

这篇翻译遗漏了一些内容(可能因为作者问题,也可能因为原文更新问题):

  1. 没有翻译原文折叠的部分,那里描述了你想知道的“不健壮性”的具体例子,但主要讲了 #[may_dangle] 的用法。

  2. struct Vec { data: *const T, len: usize, cap: usize, } 这个例子还有后半部分:

But ever since RFC 1238, this (add an extra PhantomData) is no longer true nor necessary. Adding an extra _owns_T: PhantomData field is thus superfluous and accomplishes nothing.

所以,这是一个不需要 PhantomData 的反例!

我总结一下这两部分吧:在手动实现 Drop 中,PhantomData 不再必要,因为它对 drop 没有影响,反而可能会考虑 #[may_dangle]。

然后综合全文:PhantomData 的一个重要应用是描述型变。

1 共 4 条评论, 1 页