循环内的生命周期之蠢笨编译器——新解释 #397
yingmanwumen
started this conversation in
内容建议
Replies: 1 comment
-
结构体的粒度是可以细化到字段,只要不涉及跨函数传递:) 你的其它想法也挺好的,我仔细品味下 |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
在stackoverflow上有一个问题提及了类似的错误:https://stackoverflow.com/questions/66167634/how-to-fix-was-mutably-borrowed-here-in-the-previous-iteration-of-the-loop
这是另一段本质相同的错误代码:
s[i] += 1;
可以看作是对s[i]
进行一次可变借用(相当于C语言里面a[i]
等价于*(a + i)
)然后在可变借用上加一个1
。依照常理而言,
s[i] += 1;
的&mut
效果在分号之后就结束了,然后&s[i]
被添加到t
后面。报错是:
将这段代码分成两部分写:
完美通过编译。
根据sf里的描述:
&mut self means not just mutable, but exclusive and mutable
,对于被&mut
了的变量而言,它实际上是一个排它的效果、上锁的效果——而且任何被“带出去”了的引用都会延长上锁的时间,因为借用检查器没有办法确定原数组会不会在“带出去”的引用存在的期间忽然消失掉(暂时还没有办法进行保证),如果发生了这个场景,那么内存就不安全了,因为引用还在、内存却消失了。其实这样看下来,不管是不是
mut
,只要是&
,都会存在这样的问题。所以这一段代码中可以这么理解:s[0] += 1;
,没有问题,t.push(&s[0]);
,没有问题,但是这个时候s
就被&
给锁住了s[1] += 1;
,需要s[1]
的&mut
,但是s
已经被上一个循环的&s[0]
给锁住了(检测到上一个循环的&
还存在着),没有办法使用本质上和这样子的一段代码很相似:
与此相似的还有另一段代码:
报错:
为什么中间变量很容易引发这样子的错误呢?我个人的理解是这样子的:
在一个函数里面,如果循环内的没有中间变量,那么数组的引用关系是简单的,数组处理完毕后要么直接返回一个
&mut
、要么干干净净地进入下一轮循环;如果有了中间变量,那么函数没有办法保证这个中间变量会做一些什么事情(我个人的估计,在编译器的层面上,&arr
和&arr[_]
的借用检查的粒度有可能是一致的,也就是说,一个&mut
借用,借用检查器在这个粒度下没有办法保证&mut
借用不会调用诸如clear()
之类的函数,因为这些都是允许的,借用检查器只管看接口对不对、签名对不对,不管这个方法干了什么事情),数组的引用流向就会产生分支,一个分支流向return
,另一个分支流向下一个循环。如果
rust
有办法确认在&mut arr[_]
的情况下,arr
不会忽然消失不见(也就是确认&mut arr[_]
的中间变量不会和&mut arr
一样可以调用clear()
之类的函数),那么,大部分此类问题大概都能迎刃而解。我记得struct
好像已经可以将粒度细化到某个具体的字段了,希望在下一个大版本到来的时候能彻底解决。这就是rust借用机制以及生命周期机制下的难题了,编译器能做的事情始终是有限的,我们要么顺着它,要么用
unsafe
(但是后果自负)不一定正确,但是我觉得比较有道理。
Beta Was this translation helpful? Give feedback.
All reactions