Stacked Borrows: An Aliasing Model for Rust笔记-第一部分(2)

共享引用

到目前为止,我们仅考虑了可变引用和原始指针。我们已经看到,stack borrow对可变引用强制执行一种唯一性形式,足以证明程序转换可以对未知代码周围的内存访问进行重新排序。在本节中,我们将研究共享引用。目标是强制它们是只读的,以再次证明程序转换的合理性,该转换将未知代码周围的内存访问重新排序。
就像我们为可变参考所做的一样。我们通过重述借阅检查器以避免提及生命周期的方式来

重新定义共享引用的stack原则:

每次使用引用(及其派生的所有内容)都必须在引用对象的下一次更改使用之前(之后)创建了引用),此外,不得将引用用于可变。

要了解这种情况如何发生,让我们再次考虑一个涉及整数引用的简单示例:

尽管有x,shared1和shared2别名的事实,但该程序在第8行之前都可以运行,但是在第9行,则违反了共享引用的stack原则:在第9行中使用了对shared1的引用后,才在第8行中对引用对象进行了更改.

 

使用Stacked Borrows进行建模,我们介绍了可以存在于Borrow stack中的另一种item

新的Item SharedRO(t)(“共享只读”)指示允许使用t标记的引用从与栈关联的位置读取但不能写入。我们还为SharedRW配备了tag。这意味着我们现在可以谈论“一个Item的tag”,这个说法在READ-1中很有用


在NEW-MUTABLE-RAW-1中,我们只把SharedRW()推入stack而不是SharedRW(目前为止,这是唯一一种SharedRW项  Item,⊥代表没有值)。
我们将现有的Stacked Borrow规则修改如下:

 

规则(NEW-SHARED-REF-1)。每当从某个现有指针值Pointer(location.t)创建一个新的共享引用时(&expr)。首先,这被视为对该指针值的读取访问(因此,我们遵循下面的READ-1)。然后,我们选择一些新标签t',使用Pointer(location,t')作为共享引用的值,并将SharedRO(t')添加到l的栈顶。


规则(READ-1)。每当读取值为Pointer(location,t)的指针时,tag t的Item(即Unique(t),SharedRO(t)或SharedRW(t))都必须存在于t的栈中。

将item弹出堆栈,直到tag t上方的所有item都为SharedRO(_)位置。

如果堆栈中不存在这个Item,则程序违反了stack原则。 (此规则胜过现有的use-2,后者仅用于写操作。)

 

请注意,我们保留写规则不变,这意味着即使SharedRO(t)在stack中,标记为t的引用也不能用于写,因为这需要Unique(t)或SharedRW(t)。
READ-1的关键点(与UsE-2的关键区别)在于,使用Pointer(location,t)进行读取不会导致标签t位于堆栈顶部的item结束!上面可能有一些SharedRO。这反映了这样一个事实,即可以使用两个共享的引用进行阅读而不会“干扰”
彼此;它们不会将另一个引用的项目弹出栈。相反,写访问(仍由use-2支配)要求带有用于访问的指针的标签的项成为堆栈中的头项。
因此,此系统保持的关键不变式是,如果栈中有任何SharedRO,则它们在顶部都相邻。请注意,所有push另一种item(创建可变引用或原始指针)的操作都算作写访问,因此它们首先会通过弹出所有SharedRO来使某些Unique()或SharedRW()成为堆栈的顶部在他们上面。从不将Unique()或SharedRW()推到SharedRO()的顶部。


使用这些规则,示例程序执行如下:

观察用于读取引用的规则如何使x,shared1和shared2愉快地共存
(甚至允许使用“ XYXY”模式),但是当我们写入x时,SharedRO()项将从堆栈中删除,并且不再使用相应的共享引用。

 

利用只读共享引用的优化

要查看Stacked Borrows对共享引用的处理有何帮助,让我们考虑一个可以从优化中受益的函数,利用共享引用为只读:

这次,我们使用闭包f来反映这样的想法:任意代码可以在第3行(我们第一次读取x)和第5行(我们希望优化计算)之间运行。
* x / 3。与可变引用不同,我们甚至为未知代码提供了对我们的引用x的访问权限!但是,它是一个只读引用,因此f应该不能通过它进行写操作。
再次,我们可以设计一个反例,禁止在幼稚的语义下进行此优化

 

在这里,我们创建一个闭包,Rust中的语法为args |。身体。有趣的部分在第6行和第7行,它们是我们传递给example2的闭包f的主体。在那里,我们通过调用transmute(Rust的sunchecked强制转换操作)来规避不能写入共享引用的限制。这使我们可以将(只读)共享引用转换为(可写)原始指针,然后进行写入。
但是,在堆积借款下,该程序具有未定义的行为(正如我们希望的那样)
转换不会影响指针的标签t或借位堆栈。从而。在第7行中,当我们遵循use-2时,在借用堆栈上找不到唯一(t)。标签为t的唯一项目是第5行中的重新标签添加的SharedRO(t)。
更详细地。这是针对example2进行优化的反例的逐步执行(闭包使控制流更加复杂-方括号中的数字表示执行的顺序):

 

共享参考文献优化的证明草稿

再次,我们排除了所需优化的一个特定反例,但我们真正需要做的是证明该优化在任何可能的情况下都是正确的。再次是相关代码:

现在,参数如下:
(1)假设在第2行的重新标记之后,x的值为Pointer(location,t)。我们知道SharedRO(t)
在location的借用堆栈的顶部。让我们调用存储在该位置s中的当前标量值。目的是表明在第5行中仍存储值s。
(2)在执行f的同时,我们知道任何写访问都将从堆栈中弹出所有SharedRO。
这依赖于堆栈中所有SharedRO位于顶部的不变性。因此,我们得出结论,只要SharedRO(t)在堆栈中,存储在t处的值仍为s
3)最后,在第5行中,我们假定SharedRO(t)仍在堆栈中,因为否则程序将违反Stacked Borrows(不存在带有该标签的其他项)。因此,我仍然存储s,这证明了优化的合理性。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值