代码不长,直接贴这里了。我的疑惑在下面做了标注,主要还是生命周期的问题。
fn strtok<'a>(s: &'a mut &str, pat: char) -> &'a str {
match s.find(pat) {
Some(i) => {
let prefix = &s[..i];
let suffix = &s[i + pat.len_utf8()..];
*s = suffix;
prefix
}
None => {
let prefix = *s;
*s = "";
prefix
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn it_works() {
let mut s = "hello world";
let s2 = &mut s;
let s1 = strtok(s2, ' '); // a
assert_eq!(s1,"hello"); // b b、c两句交换位置就会报错,说是在a处
assert_eq!(*s2,"world"); // c 可变借用后,又在c处存在不可变借用
}
}
1
共 11 条评论, 1 页
评论区
写评论严格说来,你遇到的问题与Non-lexical lifetimes简称NLL相关,与Copy Trait没太大关系。
具体可参阅LRFRC系列:全面理解Rust生命周期: https://mp.weixin.qq.com/s?__biz=MzIxMTM0MjM4Mg==&mid=2247483826&idx=1&sn=5b3fe0e8b51d5cafb01db1f4a441be66中
4.Non-lexical lifetimes 部分
👇
苦瓜小仔: 是的,
https://doc.rust-lang.org/nightly/std/marker/trait.Copy.html
https://doc.rust-lang.org/std/mem/fn.drop.html
是的,
https://doc.rust-lang.org/nightly/std/marker/trait.Copy.html
https://doc.rust-lang.org/std/mem/fn.drop.html
我了个去,我这才突然反应过来了 共享引用(&T)具有 Copy trait ,所以每次使用 &T 都是使用 &T 的复制品 这句话的意思,其实drop(a),这里传进去的就是a的复制品,真正的a其实没有动到。那么是不是所有Copy trait的,手动drop其实没用?
别想得太复杂了。
我举 Copy trait 的例子是想说:
共享引用(&T)具有 Copy trait ,所以每次使用 &T 都是使用 &T 的复制品
把这句话换成任何具有 Copy trait 的类型都成立。如果你能理解以下代码为什么能通过,那么能理解 s1 为什么一直存在。
又是什么原因,让栈上的 s1 一直有效的呢?
Copy trait 只让共享引用一直复制,不会保持引用一直有效。
所以 s1 一直有效是因为:原数据存在(即变量 s 的生命周期至少比 s1 要长)。
总而言之,
fn strtok<'a>(s: &mut &'a str, pat: char) -> &'a str { ... }
这个函数签名可以联系起共享引用的 Copy trait,让生命周期'a
的含义更清晰 —— strtok 所返回的引用就是直接指向原数据的引用,你那种标注达不到这种效果。对s、s1、s2,我理解的是在栈上,它们都是各自的区域,哪怕这个区域存储的都是同一块堆上内存的地址。drop(s1)如果不能标记栈上的s1无效,又不能释放堆上的内存,那么它什么都没做?!又是什么原因,让栈上的s1一直有效的呢。
谢谢瓜仔大佬的认真回复,说说我的理解,也算是交作业了:
&mut str:指向str的可变引用,意思是可变的&str
mut &str:可变的字符串,&str指向的内容可变
&mut &'a str:指向&str的可变引用,保证&str的生命周期为'a并周期内有效
&'a &mut str:指向&str的可变引用,保证&mut str的生命周期为'a并周期内有效
例子中,我只需要&str,即s本身和函数返回具有同样生命周期即可,之前的错误是我搞成了保证可变引用本身的生命周期了,当下一句*s2时,就在原有&mut &s2的基础上多了一个&&s2,导致编译器报错。
不过这里还是不明白为啥drop之后还能访问s1。
就算s2传入strtok时,函数是把堆上s0的数据复制了一份,处理完了让s1指向新复制的这份数据。那么,drop时发生了什么?我的理解是至少释放了堆上s1指向的那份新数据吧?也应该s1无效了啊。 我以为的drop(s1)如下,不管是否支持Copy Trait,我认为drop都是释放了堆内存的。不过显然不对,否则上面最后两个调用就该出错: 1、释放s1指向的堆内存 2、标记栈上的s1为无效(猜的) 所以,
和drop时,到底发生了什么?希望知道的大佬指点一下。
延伸的细节(关于 Copy trait)见:
playground
生命周期这种概念就是多思考才会理解,尤其像这样从很小的例子中理解透彻才会掌握。
有道理!学到了。
👇
苦瓜小仔:
因为你标注的生命周期在这里比所需的要严格一些。更合适的标注应该是这样。
因为你标注的生命周期在这里比所需的要严格一些。更合适的标注应该是这样。
这只是一次练习,是没想明白为什么。
--
👇
lan: 你可以用一个大括号把ab两行括起来,但如果互换bc两行就不行了。因为借用的生命周期止于最后一次被使用时。
你可以用一个大括号把ab两行括起来,但如果互换bc两行就不行了。因为借用的生命周期止于最后一次被使用时。