陈天 · Rust 编程第一课
陈天
Tubi TV 研发副总裁
23195 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 65 讲
基础篇 (21讲)
陈天 · Rust 编程第一课
15
15
1.0x
00:00/00:00
登录|注册

加餐|愚昧之巅:你的Rust学习常见问题汇总

Rust版本更新
Rust编译二进制大小
调试应用程序
声明全局变量
使用unsafe
使用unwrap()
Rust字符串
函数返回引用
"use of moved value"错误
创建双向链表
其它问题
调试工具
数据结构问题
生命周期问题
所有权问题
生命周期
所有权
内存管理
基本语法
思考题
常见问题解决
学习节奏调整
示例项目
Rust知识
陈天
愚昧之巅:你的Rust学习常见问题汇总
参考文章

该思维导图由 AI 生成,仅供参考

你好,我是陈天。
到目前为止,我们已经学了很多 Rust 的知识,比如基本语法、内存管理、所有权、生命周期等,也展示了三个非常有代表性的示例项目,让你了解接近真实应用环境的 Rust 代码是什么样的。
虽然学了这么多东西,你是不是还是有种“一学就会,一写就废”的感觉?别着急,饭要一口一口吃,任何新知识的学习都不是一蹴而就的,我们让子弹先飞一会。你也可以鼓励一下自己,已经完成了这么多次打卡,继续坚持。
在今天这个加餐里我们就休个小假,调整一下学习节奏,来聊一聊 Rust 开发中的常见问题,希望可以解决你的一些困惑。

所有权问题

Q:如果我想创建双向链表,该怎么处理?
Rust 标准库有 LinkedList,它是一个双向链表的实现。但是当你需要使用链表的时候,可以先考虑一下,同样的需求是否可以用列表 Vec<T>、循环缓冲区 VecDeque<T> 来实现。因为,链表对缓存非常不友好,性能会差很多。
如果你只是好奇如何实现双向链表,那么可以用之前讲的 Rc / RefCell (第 9 讲)来实现。对于链表的 next 指针,你可以用 Rc;对于 prev 指针,可以用 Weak
Weak 相当于一个弱化版本的 Rc,不参与到引用计数的计算中,而 Weak 可以 upgrade 到 Rc 来使用。如果你用过其它语言的引用计数数据结构,你应该对 Weak 不陌生,它可以帮我们打破循环引用。感兴趣的同学可以自己试着实现一下,然后对照这个参考实现
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Rust学习中常见问题汇总 本文深入探讨了Rust学习中常见问题,包括所有权、生命周期、数据结构等多个方面。针对每个问题提供了解决方法和建议,强调了在生产环境中使用模式匹配和错误处理的重要性,以及避免使用unsafe代码的建议。文章还介绍了调试工具的使用方法,解释了Rust编译出的二进制大小和运行速度的问题。此外,还提到了Rust语言不断发展的特性,以及对最新版本的建议。通过解答常见问题,帮助读者更好地理解和应用Rust语言的特性。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《陈天 · Rust 编程第一课》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(27)

  • 最新
  • 精选
  • 秋声赋
    置顶
    我看到用了很多的宏,这个有没有详细的说明呢?

    编辑回复: 06那篇有简单讲到宏,不过因为宏是高级内容在课程里目前没有详细讲解,你可以自己找资料学习。之后老师有空会补充一篇关于宏的内容,也欢迎期待~

    2022-01-11
    2
  • lisiur
    第一个,没有标注生命周期,但即使标注也不对,因为返回值引用了本地已经 drop 的 String,会造成悬垂指针问题; 第二个,和第一个类似,因为参数是具有所有权的 String,该 String 会在函数执行完后被 drop,返回值不能引用该 String; 第三个,因为 Chars 的完整定义是 Chars<'a>,根据生命周期标注规则,Chars 内部的引用的生命周期和参数 name 一致,所以不会产生问题。

    作者回复: 非常棒!

    2021-09-17
    45
  • 乌龙猹
    陈老师,啥时候再出一门 Elixir 编程的第一课啊

    作者回复: :) 做这一门课我就感觉已经竭尽全力了,做完估计要休半年才能缓过来

    2021-09-17
    7
    21
  • Arthur
    lifetime1: name为函数内部的临时变量,类型是String,函数返回值为其引用,但引用的变量name生命周期在函数结束时,会被drop,因此此处引用失效,无值可借; lifetime2: name为具有所有权的参数,类型是String,在函数被调用时,所有权会move给name,在函数执行结束时,name会被drop,因此返回值的引用还是无值可借,编译器无法推导出合理的生命周期; lifetime3: chars()返回的iterator具有和函数参数name相同的生命周期,name本身又是一个借用,真正具有所有权的变量存活的比函数久,因此这个函数可以编译通过 参考材料: 编译器报错信息 ```plain | 12 | fn lifetime1() -> &str { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from --> src/main.rs:18:31 | 18 | fn lifetime2(name: String) -> &str { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments ``` 标准库具体实现 ```rust // Returns an iterator over the chars of a string slice. pub fn chars(&self) -> Chars<'_> // Converts the given value to a String. fn to_string(&self) -> String ```

    作者回复: 非常好,目前最棒的答案!

    2021-09-17
    10
  • gnu
    lifetime1: 返回的引用是在 lifetime1 里被分配,lifetime1 结束后引用就被回收,所以错误。 改为转成 string 后返回。 ``` fn lifetime1() -> String { let name = "Tyr".to_string(); name[1..].to_string() } ``` lifetime2: 函数参数是 String,编译器无法通过参数确定返回值 &str 的生命周期。 修改为 ``` fn lifetime2(name: &String) -> &str { &name[1..] } ``` lifetime3: 返回 Chars 类型的生命周期与参数 name 关联,所以正确。

    作者回复: 非常棒!修改得也很棒!

    2021-09-17
    10
  • 彭亚伦
    关于String 和 &str相关的各种问题, 我的经验, 一个核心原因是因为 String 实现了Deref<Target = str>, String和&str是通过这个Deref Trait建立了互换的关系; 这样做带来了很多便利, 同时也有个side effect, 就是当参数要求是 &str 时, 实参可能是&str也可能是&String, 而两者的生命周期明显是不一样的, 于是就产生了各种看似比较难以琢磨的问题.

    作者回复: &str / &String 生命周期为什么不一样?生命周期和类型无关,和数据有关。我猜你说 &str 和 &String 声明周期不一样,是因为我们会使用 string literal,比如: let s1 = "hello"; let s2 = "hello".to_stirng(); 这两者生命周期不一样是因为 "hello" 本身是 &'static(编译时放在 RODATA section 中),而 s2 复制了一份 "hello" 放在堆上。

    2021-10-26
    5
  • 记事本
    老师,关于智能指针一些问题: 数据放在堆上,返回指针给栈上的结构体 智能指针有个特点,*解耦到原型,&*就是获取数据的引用,单&栈上结构体的地址 *因为会解耦出原型,所以原数据是否实现copy trait,否则会move,智能指针就没有所有权了

    作者回复: 是的,所以 * 不能直接使用在诸如 String / Vec 这样数据结构的引用上。当你尝试这样做,编译器会报错:数据被 borrow,但又被 move,且没有实现 copy,所以错误。 ```rust fn main() { let s = "hello".to_string(); let r1 = &s; let s1 = *r1; println!("{:?}", s1); } ``` 所以这里会编译出错,而不是移动所有权。你不能一边借用,一边移动所有权。

    2021-09-17
    2
    5
  • Kerry
    例子一: 1. &str生命周期不明确 2. 返回了局部函数拥有所有权的引用,也是生命周期问题 可改为: fn lifetime1() -> &'static str { let name = "Tyr"; &name[1..] } 例子二: 函数参数不是引用类型,而且String没有实现Copy Trait,传参的时候会把形参的所有权给到实参,这时候跟例子一是一样的。解决办法是把形参定义为引用类型,如&str(&String也不是不行): fn lifetime2(name: &str) -> &str { &name[1..] } 注意这里例子二不用指定返回值的生命周期,因为编译器可以从参数列表自动推断。 例子三: Chars是字符串切片迭代器,生命周期与&str是一致的,这一点可以从签名中看出: // std::str::chars pub fn chars(&self) -> Chars<'_> // std::str::Chars pub struct Chars<'a> { pub(super) iter: slice::Iter<'a, u8>, }

    作者回复: 👍 非常好。要注意的是例子 1 的改法并不是通用的,它只适合少数可以 &'static 的情况。

    2021-09-18
    3
  • 罗杰
    比较简单的问题,第一个 name 在函数里面创建的 String,函数返回时就释放掉了,这是最直白的悬垂引用。第二个 name 是从调用者 move 过来的 String,进入该函数,所有权就归函数了,返回时 name 也将被释放。第三个 name 不用加生命周期标注可以正常工作,参数是引用,返回的数据与该参数的生命周期相同,没有问题,可以编译通过。

    作者回复: 非常棒!

    2021-09-17
    3
  • 亚伦碎语
    对&str 和 &String的区别,更新一点: String可以动态的调整内存大小。 str不能resize. &str直接是指到了String存储的引用,&String是对于String内存对象的引用。 参考: https://users.rust-lang.org/t/whats-the-difference-between-string-and-str/10177/8

    作者回复: 嗯。其实不必这么记。还是要抓住 String 和 str 的实质。一个拥有所有权(自然也可以 put_str),一个没有所有权,只是切片(自然不能 resize 别人的东西)。

    2021-09-23
    2
收起评论
显示
设置
留言
27
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部