上一个帖子里面 请教一个生命周期的问题,我的问题是self.target的生命周期为什么是'b,看了大佬们的回答,我大致明白了。现在我强制把返回的生命周期跟'a挂钩,那对于target的借用就会扩大到dbg!(foo_out)这个地方,这时如果中间有一个对于target的mutable借用,编译器应该会报错,实际测试并没有,这是为什么呢?
use std::mem::transmute;
pub struct Writer<'a> {
target: &'a mut String,
}
impl<'a> Writer<'a> {
fn indent<'b>(&'b mut self) -> &'a String {
unsafe {
transmute (&*self.target)
}
}
}
fn main() {
let mut foo_in = String::from("abc");
let mut foo = Writer {
target : &mut foo_in
};
let foo_out = foo.indent();
foo.target.push('d'); // 这儿对foo.target做了reborrow,应该会报错啊
dbg!(foo_out);
}
1
共 9 条评论, 1 页
评论区
写评论这个编译不过是因为 'a 不可能超过 foo_in 的 liveness scope
请看下面的改法:
你要不要猜一猜为什么transmute是unsafe函数?
--
👇
QingJuBaiTang: ```rust // 完整的函数原型应该是 impl<'a> Writer<'a> { fn indent<'b>(writer: &'b mut Writer<'a>) -> &'a String { unsafe { transmute (&*writer.target) } } }
编译时对函数调用只看签名, 编译器不会知道你返回值借用了啥。
其实它也不关心。 在你这个例子里其实是 indent 的实参决定了 foo.target 能不能访问。
这个我再前面已经说了啊
因为有'a outlives writer.target,所以我理解函数对writer.target的借用会持续到函数结束,但是中间又发生了一次对target的借用(foo.target.push('d')),这不是违反借用规则了吗
你这个函数的原型只说明了
foo_out outlives 'b
也就是说, 'b 在 foo_out 之前销毁。
那么, 'b 当然可以在
foo.target.push('d')
前销毁既然 'b 销毁了, 那么
foo.target.push('d')
自然可以编译成功换句话说, 编译函数时只看函数原型, 编译器不知道返回的引用借用的是什么。 决定 foo.target 什么时候允许访问的是
&'b mut self
。为何
不行也很显然了
因为 'b 一致延续到最后, 那么自然
&'b mut self
也还在, 因此foo.target.push('d')
编译不了补充一下,如果我把'a换成'b,结果是符合预期的,编译会报错。
忽略transmute的那个写法,那不是我想问的。 我的意思是我返回了一个'a生命周期的引用,这个引用来自于Writer<'a>,也就是Writer.{&'a mut target}。 也就是说let foo_out = foo.indent()这个调用会将target会被借用到最后一行(dbg!(foo_out);),但是中间又对target做了一次借用(foo.target.push('d');),也就是说对target可变借用了2次,理论上编译会报错,实际上并没有报错
--
👇
苦瓜小仔: > 现在我强制把返回的生命周期跟'a挂钩
不知道你在问些什么,但至少这句话与代码里的 transmute 背道而驰。但凡看一看 transmute 的文档(别告诉我你只看那些文字,里面的超链接都不点进去看)
transmute 可以转化任何生命周期 —— 所以 foo.target.push('d') 不会在你写的情况下报错不是完全在意料之中吗。
甚至大胆点,对于以下 unbounded lifetime 签名
fn indent<'b, 'c>(&'b mut self) -> &'c String
代码可以编译,我也不会感到奇怪。
毕竟 safe 编译器要你写标注契约,你偏要以 unsafe 绕道不写,后果自负咯。
不知道你在问些什么,但至少这句话与代码里的 transmute 背道而驰。但凡看一看 transmute 的文档(别告诉我你只看那些文字,里面的超链接都不点进去看)
transmute 可以转化任何生命周期 —— 所以 foo.target.push('d') 不会在你写的情况下报错不是完全在意料之中吗。
甚至大胆点,对于以下 unbounded lifetime 签名
fn indent<'b, 'c>(&'b mut self) -> &'c String
代码可以编译,我也不会感到奇怪。
毕竟 safe 编译器要你写标注契约,你偏要以 unsafe 绕道不写,后果自负咯。