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

03|初窥门径:从你的第一个Rust程序开始!

属性宏
派生宏
函数宏
分支跳转
循环
函数调用
顺序执行
数据结构
变量和函数
支持宏编程
强类型语言但支持类型推导
语法偏 C/C++ 风格
使用 cargo 工具管理项目
过程宏的三种方式
改进代码获取用户提供的信息
抽取重复代码
crate 和 workspace
使用 mod 组织代码
错误处理
模式匹配
控制流程
基本语法和基础数据类型
Rust 的基本特点
使用任何编辑器来撰写 Rust 代码
使用 cargo new 新建 Rust 项目
安装 Rust 工具链
"get hands dirty"
置身于语言的环境中
Rust 支持声明宏和过程宏
思考题
Rust 项目的组织
Rust 基本内容
第一个 Rust 程序
Rust 环境设置
学习语言最好的捷径
参考资料
Rust 语言学习
初窥门径:从你的第一个Rust程序开始!

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

你好,我是陈天。储备好前置知识之后,今天我们就正式开始 Rust 语言本身的学习。
学语言最好的捷径就是把自己置身于语言的环境中,而且我们程序员讲究 “get hands dirty”,直接从代码开始学能带来最直观的体验。所以从这一讲开始,你就要在电脑上设置好 Rust 环境了。
今天会讲到很多 Rust 的基础知识,我都精心构造了代码案例来帮你理解,非常推荐你自己一行行敲入这些代码,边写边思考为什么这么写,然后在运行时体会执行和输出的过程。如果遇到了问题,你也可以点击每个例子附带的代码链接,在 Rust playground 中运行。
Rust 安装起来非常方便,你可以用 rustup.rs 中给出的方法,根据你的操作系统进行安装。比如在 UNIX 系统下,可以直接运行:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
这会在你的系统上安装 Rust 工具链,之后,你就可以在本地用 cargo new 新建 Rust 项目、尝试 Rust 功能。动起手来,试试用 Rust 写你的第一个 hello world 程序吧!
fn main() {
println!("Hello world!");
}
你可以使用任何编辑器来撰写 Rust 代码,我个人偏爱 VS Code,因为它免费,功能强大且速度很快。在 VS Code 下我为 Rust 安装了一些插件,下面是我的安装顺序,你可以参考:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文以实际代码示例为主线,通过动手编写程序来快速了解Rust编程语言的基本特点和基础知识。作者介绍了如何从头开始学习Rust编程语言,通过编写第一个Rust程序来快速入门。读者可以通过在VS Code下安装Rust相关插件,并运行一个实用的Rust程序,通过HTTP请求Rust官网首页并将HTML转换成Markdown保存,来体会Rust的基本特点。文章还介绍了Rust的数据结构定义、控制流程、模式匹配和错误处理等内容,展示了Rust作为一种现代编程语言的强大特性和灵活应用。此外,作者还概述了Rust开发的基本内容,包括变量、函数、数据结构等。最后,建议读者逐段写下示例代码并运行,以加深对Rust的印象。整体而言,本文通过实际操作和示例代码,为读者提供了全面的学习参考,使其能够快速了解Rust编程语言的基本概念和特点。

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

全部留言(108)

  • 最新
  • 精选
  • 树静风止
    置顶
    2022-12-28,这是一条较新的错误处理解决留言。 如果你是在windows环境下cargo run课程中的代码发现出现以下错误: error: linking with `x86_64-w64-mingw32-gcc` failed: exit code: 1 网上解决方案是安装x86_64-pc-windows-msvc,但是你已经成功安装,却依然报错。 原因是除了安装msvc工具链以外,你还需要切换rust当前默认的工具链。 # 显示当前安装的工具链信息 rustup show # 设置当前默认工具链 rustup default stable-x86_64-pc-windows-msvc 这样你就可以正常编译运行了。

    编辑回复: 👍 给你置顶了

    2022-12-28归属地:北京
    4
  • Roy Liang
    置顶
    export RUSTUP_DIST_SERVER=https://mirrors.sjtug.sjtu.edu.cn/rust-static export RUSTUP_UPDATE_ROOT=https://mirrors.sjtug.sjtu.edu.cn/rust-static/rustup rust国内安装必备环境配置

    作者回复: 👍 谢谢提醒!我倒没有关注国内需要镜像的问题。

    2021-08-27
    6
    48
  • 赵岩松
    置顶
    文中的"Rust 没有语句(statement),只有表达式(expression)"表述我认为是错误的, 我猜这里想表达的内容应该类似于《Rust程序设计语言中》的如下语句 "Rust 是一门基于表达式(expression-based)的语言,这是一个需要理解的(不同于其他语言)重要区别" 但是我的观点是:Rust既存在语句也存在表达式 我的依据为书中接下来的内容 "语句(Statements)是执行一些操作但不返回值的指令。表达式(Expressions)计算并产生一个值" 书中还附带了一个简单的例子,在这里我大体描述一下 `let y = 6;`中,6为一个表达式,它计算出的值是 6,`let y = 6;`做为一个整体是一条语句,他并不返回值,所以我们不能在Rust中这样书写`let x = (let y = 6);` 关于这个问题还有来自交流群内"Tai Huei"提供的截图中的文字作为依据 "Statements are instructions that do something, they do not return a value. Expressions evaluate to a value, they return that value" "Rust is an expression-oriented language. This means that most things are expressions, and evaluate to some kind of value. However, there are also statements. -Steve Klabnik(member if the Rust core team)"

    作者回复: 对,这里表达有错误。let 的确是一个 statement。见:https://doc.rust-lang.org/reference/statements.html。我想更着重表达的是,很多语言的 while loop,for loop,if else 都是语句,但 Rust 下它们是表达式,它们会返回最后一个表达式的值(即便是 unit)。我忽视了 let/static/const/fn 这样的定义,它们是纯粹的语句。回头我改一下。

    2021-08-27
    4
    60
  • pedro
    我想很多人不会被课后问题所困扰而是被 Copy 和 Clone,初学时我也很纠结,这里贴上某位大佬的总结: Copy 和 Clone 两者的区别和联系有: Copy内部没有方法,Clone内部有两个方法。 Copy trait 是给编译器用的,告诉编译器这个类型默认采用 copy 语义,而不是 move 语义。Clone trait 是给程序员用的,我们必须手动调用clone方法,它才能发挥作用。 Copy trait不是你想实现就实现,它对类型是有要求的,有些类型就不可能 impl Copy。Clone trait 没有什么前提条件,任何类型都可以实现(unsized 类型除外)。 Copy trait规定了这个类型在执行变量绑定、函数参数传递、函数返回等场景下的操作方式。即这个类型在这种场景下,必然执行的是“简单内存拷贝”操作,这是由编译器保证的,程序员无法控制。Clone trait 里面的 clone 方法究竟会执行什么操作,则是取决于程序员自己写的逻辑。一般情况下,clone 方法应该执行一个“深拷贝”操作,但这不是强制的,如果你愿意,也可以在里面启动一个人工智能程序,都是有可能的。 链接:https://zhuanlan.zhihu.com/p/21730929

    作者回复: 我会在讲所有权的时候介绍 Copy/Clone,敬请期待。

    2021-08-27
    3
    36
  • GengTeng
    2. 一个模式匹配就行了,还做到了 panic-free: let args = std::env::args().collect::<Vec<String>>(); if let [_path, url, output, ..] = args.as_slice() { println!("url: {}, output: {}", url, output); } else { eprintln!("参数缺失"); }

    作者回复: 👍 非常棒

    2021-09-03
    2
    13
  • Kerry
    课后习题需要自己查一点接口资料,结合错误信息来逐步解决编译问题。 问题一: 重复的abc计算代码可以重构为如下函数: ```rust fn next_fib(a: &mut i32, b: &mut i32) { let c = *a + *b; *a = *b; *b = c; } ``` 以for in循环为例,用法如下: ```rust fn fib_for(n: u8) { let (mut a, mut b) = (1, 1); for _i in 2..n { next_fib(&mut a, &mut b); println!("next val is {}", b); } } ``` 问题二: ```rust fn main() -> Result<(), Box<dyn std::error::Error>> { let args: Vec<String> = env::args().collect(); if args.len() < 3 { println!("Usage: url outpath"); return Ok(()) } args.iter().for_each(|arg| { println!("{}", arg); }); let url = &args[1]; let output = &args[2]; println!("Fetching url: {}", url); let body = &reqwest::blocking::get(url)?.text()?; println!("Converting html to markdown..."); let md = html2md::parse_html(body); fs::write(output, md.as_bytes())?; println!("Converted markdown has been saved in {}.", output); Ok(()) } ``` 好歹是跑起来了……装了智能提示真不错,有better impl的建议 :)

    作者回复: 赞!

    2021-08-27
    10
  • Quincy
    use std::fs; use structopt::StructOpt; #[derive(StructOpt, Debug)] #[structopt(name="scrape_url")] struct Opt { #[structopt(help="input url")] pub url: String, #[structopt(help="output file, stdout if not present")] pub output: Option<String>, } fn main() { let opt = Opt::from_args(); let url = opt.url; let output = &opt.output.unwrap_or("rust.md".to_string()); println!("Fetching url: {}", url); let body = reqwest::blocking::get(url).unwrap().text().unwrap(); println!("Converting html to markdown..."); let md = html2md::parse_html(&body); fs::write(output, md.as_bytes()).unwrap(); println!("Converted markdown has been saved in {}.", output); }

    作者回复: 使用了 StructOpt,👍 可以考虑为 url 加上: #[structopt(..., parse(try_from_str = parse_url)], 验证一下。

    2021-08-27
    3
    10
  • 慢动作
    字符串字面量为什么有into方法,这中间经历了什么过程?看文档根本不知道这个方法哪里来的,😂。还是有点操之过急,看到不明白就瞎忙活,感觉还是得循序渐进

    编辑回复: 没事放轻松,今天先把代码跑起来就行,之后都会慢慢讲到的。之前咱们学习都是先学语法,再写程序。试试课程这个新玩法啊,啥也别说就是写,先直观感受一波,之后再一点一点抽丝剥茧😁

    2021-08-27
    8
  • 🔥神山 | 雷神山
    2. 在 scrape_url 的例子里,我们在代码中写死了要获取的 URL 和要输出的文件名,这太不灵活了。你能改进这个代码,从命令行参数中获取用户提供的信息来绑定 URL 和文件名么?类似这样: ```rust use std::env; use std::error; use std::fs; use std::process; const NOT_ENOUGH_ARGS_CODE: i32 = 1; const NOT_GET_HTTP_DATA_CODE: i32 = 2; const CAN_NOT_CONVERT_MD_CODE: i32 = 3; #[derive(Debug)] struct Config { url: String, //网址 output: String, //输出文件名 } impl Config { fn new(url: String, output: String) -> Config { Config { url, output } } fn curl_url(&self) -> Result<String, Box<dyn error::Error>> { println!("Fetching url: {}", self.url); let body = reqwest::blocking::get(&self.url)?.text()?; Ok(body) } fn convert_md_file(&self, body: String) -> Result<(), Box<dyn error::Error>> { println!("Converting html to markdown..."); let md = html2md::parse_html(&body); fs::write(&self.output, md.as_bytes())?; println!("Converted markdown has been saved in {}.", self.output); Ok(()) } } fn parse_args(args: &[String]) -> Result<Config, &str> { if args.len() < 3 { return Err("Not enough arguments!"); } let url = args[1].clone(); let output = args[2].clone(); Ok(Config::new(url, output)) } fn main() { let args: Vec<String> = env::args().collect(); match parse_args(&args) { Err(err) => { println!("Error: {}", err); process::exit(NOT_ENOUGH_ARGS_CODE); } Ok(cfg) => match cfg.curl_url() { Err(err) => { println!("Error: {}", err.to_string()); process::exit(NOT_GET_HTTP_DATA_CODE); } Ok(body) => { let result = cfg.convert_md_file(body); if result.is_err() { println!("Error: Can NOT write data to {}", cfg.output); process::exit(CAN_NOT_CONVERT_MD_CODE); } } }, } } ```

    作者回复: 👍 把实现放入 impl Config {} 是个不错的优化。

    2021-08-27
    7
  • 核桃
    这里有几个小疑惑. 1.首先把函数作为参数这里,例如a(1,b(2))这样的,那么和先调用b得到结果再填入a中有什么本质区别吗?我不理解的是,分开调用好像也没什么问题,有什么场景下需要函数作为参数这样调用的? 2.派生宏这里#[derive(Debug)],这个例子中实现的是什么功能不太理解? 3.println! 这里的语法多了一个感叹号,很多语言都有println,多了这个感叹号封装多了一些什么吗?看过一些资料也不太理解 多谢了

    作者回复: 1. 函数作为参数是说 a, b 为函数,这样调用:a(1, b),把一个函数指针传给 b 作为参数。 2. #[derive(Debug)] 这里会自动生成一些代码,这些代码实现了 Debug trait。 3. println! 是一个宏,用 ! 是把它和普通函数区分开。因为 println 需要支持可变参数,Rust 不支持可变参数,所以这里用宏实现。

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