循环内的生命周期之蠢笨编译器——新解释 #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
-
在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