Rust 简明笔记
这里仅仅是笔记而已,方便快速查阅,如果还没有阅读官方文档,那么先去参阅一下官方文档吧
传送~~ 走你!!!
官方的Rust程序设计文档:https://doc.rust-lang.org/book/
官方的标准库文档:https://doc.rust-lang.org/std/index.html
官方的各种手册文档:https://www.rust-lang.org/zh-CN/learn
社区的Rust程序设计文档:https://kaisery.github.io/trpl-zh-cn/title-page.html
[目录](./SUMMARY.md)
基础语法篇
安装
linux
$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
MacOS
xcode-select --install
Windows
下载 https://www.rust-lang.org/install.html
需要安装 Build Tools for Visual Studio 2019
运行
主函数
fn main() { println!("Hello, world!"); }
文件后缀
.rs
编译
$ rustc main.rs
执行
$ main.exe
Cargo管理工具
创建一个项目
cargo new hello_cargo
编译项目
cargo build
运行项目
cargo run
快速检查代码
cargo check
编译发布
cargo build --release
变量
定义一个变量
#![allow(unused)] fn main() { let x = 5; }
{% hint style="warning" %} 变量x默认是不可变的,不可以改变变量的值 {% endhint %}
定义一个可以改变值得变量
let mut x = 5;
{% hint style="success" %} 至于变量的类型,是可以自动识别的 {% endhint %}
get一个新操作---变量隐藏
let x = 5
{
let x = "6"
}
{% hint style="info" %} 第二个被let申明的x把第一个x隐藏了
let可以定义重复的变量名称
同时变量的值也是可以改变的
更重要的是,当块{...}执行结束后,外面的x还是5,你说厉害不。厉害了~! {% endhint %}
常量
定义一个常量
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
{% hint style="info" %} 单词大写,下划线链接
冒号后面必须加类型 {% endhint %}
数据类型
变量类型
整型
| 8-bit | i8 | u8 |
|---|---|---|
| 16-bit | i16 | u16 |
| 32-bit | i32 | u32 |
| 64-bit | i64 | u64 |
| 128-bit | i128 | u128 |
| arch | isize | usize |
浮点
f32 和 f64
布尔型
bool
char
let c = 'z';
复合类型
元组 tuple
#![allow(unused)] fn main() { let tup: (i32, f64, u8) = (500, 6.4, 1); let tup = (500, 6.4, 1); let (x, y, z) = tup; // 读取 let five_hundred = x.0 }
函数
定义
#![allow(unused)] fn main() { fn hello(x: i8, y: char) -> res { x + 5 } }
{% hint style="warning" %}
- 表达式结果会直接给res {% endhint %}
注释
使用注释
#![allow(unused)] fn main() { // 一样的斜杠注释 // 没有什么特别的 }
Page 1
if
#![allow(unused)] fn main() { if number % 4 == 0 { println!("number is divisible by 4"); } else if number % 3 == 0 { println!("number is divisible by 3"); } else if number % 2 == 0 { println!("number is divisible by 2"); } else { println!("number is not divisible by 4, 3, or 2"); } // 类似三元运算 let number = if condition { 5 } else { "six" }; }
loop 死循环
#![allow(unused)] fn main() { loop { println!("again!"); } // 返回结果 let result = loop { counter += 1; if counter == 10 { break counter * 2; // 高级感 } }; }
while
#![allow(unused)] fn main() { while number != 0 { println!("{}!", number); number -= 1; } }
for
#![allow(unused)] fn main() { for element in a { println!("the value is: {}", element); } // 倒着循环 for number in (1..4).rev() { // 又是高级感 println!("{}!", number); } }
数组
数组
#![allow(unused)] fn main() { let a = [1, 2, 3, 4, 5]; let a: [i32; 5] = [1, 2, 3, 4, 5]; //i32类型 5个元素 let a = [3; 5]; // 5个3元素 let first = a[0]; //访问 }
元素的个数是固定的,元素的类型是相同的
那么如何定义一个二位的数组
#![allow(unused)] fn main() { // 嵌套元组 let a: [(i32, &str, u8);3] = [(1,"nihao",2),(1,"nihao",2),(1,"nihao",2)]; // 嵌套结构体 struct Stu { name: String, age: u64, } let students: [Stu; 2] = [Stu{name: String::from("lili"), age: 12},Stu{name: String::from("xiaoniu"), age: 12}]; println!("{},{}", students[0].name,students[0].age); println!("{},{}", students[1].name,students[1].age); }
遍历数组
fn main() { let a = [10, 20, 30, 40, 50]; for element in a { println!("the value is: {}", element); } }
可变长数组vector
#![allow(unused)] fn main() { // 新建 let v: Vec<i32> = Vec::new(); let v = vec![1, 2, 3]; //使用vec!宏定义 // 更新 v.push(4); // 获取 let third: &i32 = &v[2]; match v.get(2) { Some(third) => println!("The third element is {}", third), None => println!("There is no third element."), } // 遍历 使用&改变值 let mut v = vec![100, 32, 57]; for i in &mut v { *i += 50; } // 使用enums存储不同类型的值 enum SpreadsheetCell { Int(i32), Float(f64), Text(String), } let row = vec![ SpreadsheetCell::Int(3), SpreadsheetCell::Text(String::from("blue")), SpreadsheetCell::Float(10.12), ]; }
字符串
Rust 的核心语言中只有一种字符串类型:
str,字符串 slice,它通常以被借用的形式出现,&str
新建字符串
#![allow(unused)] fn main() { let mut s = String::new(); let s = "initial contents".to_string(); let s = String::from("initial contents"); }
更新字符串
#![allow(unused)] fn main() { let mut s = String::from("foo"); s.push_str("bar"); // 追加一个字符,结果lol let mut s = String::from("lo"); s.push('l'); // 使用 + 运算符或 format! 宏拼接字符串 let s1 = String::from("Hello, "); let s2 = String::from("world!"); let s3 = s1 + &s2; // format! 宏拼接字符串 let s = format!("{}-{}-{}", s1, s2, s3); }
索引字符串
#![allow(unused)] fn main() { let s1 = String::from("hello"); let h = s1[0]; let s = &hello[0..4]; }
遍历字符串的方法
#![allow(unused)] fn main() { for c in "abc".chars() { println!("{}", c); } for b in "abc".bytes() { println!("{}", b); } }
HashMap
高级进阶篇
description: 待补充
所有权
所有权
- Rust 中的每一个值都有一个被称为其 所有者(owner)的变量。
- 值在任一时刻有且只有一个所有者。
- 当所有者(变量)离开作用域,这个值将被丢弃。
实现了copy trait的类型,不会转移所有权
- 所有整数类型,比如
u32。 - 布尔类型,
bool,它的值是true和false。 - 所有浮点数类型,比如
f64。 - 字符类型,
char。 - 元组,当且仅当其包含的类型也都实现
Copy的时候。比如,(i32, i32)实现了Copy,但(i32, String)就没有。
引用
& 符号就是 引用,它们允许你使用值但不获取其所有权
解引用
* 符号就是解引用
借用
创建一个引用的行为
结构体
定义结构体
#![allow(unused)] fn main() { struct User { active: bool, username: String, email: String, sign_in_count: u64, } }
使用结构体
fn main() { let user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; user1.email = String::from("anotheremail@example.com"); }
结构体嵌套
fn main() { // --snip-- let user2 = User { email: String::from("another@example.com"), ..user1 }; }
..语法指剩余没有指明的字段,来自user1
元组类型结构体
struct Color(i32, i32, i32);
没有任何字段的结构体
#![allow(unused)] fn main() { struct AlwaysEqual; }
结构体实现
#[derive(Debug)] struct Rectangle { width: u32, height: u32, } impl Rectangle { // 关联函数,有点构造函数的意思 fn square(size: u32) -> Rectangle { Rectangle { width: size, height: size, } } // 实现的方法 fn area(&self) -> u32 { self.width * self.height } } fn main() { let rect1 = Rectangle { width: 30, height: 50, }; let sq = Rectangle::square(3); // 使用关联函数用 :: println!( "The area of the rectangle is {} square pixels.", rect1.area() // 调用方法用 . ); }
使用impl实现结构体,可以有多个实现,名称也可以一样
description: 枚举是enums, 匹配是match,Option是一种特殊的枚举,if let是一种特殊的match,说到匹配,这里没有Switch!!!
枚举
enums定义
#![allow(unused)] fn main() { // 可以支持很多类型 enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } // 用::取使用枚举的值 let m = Message::Write(String::from("hello")); // 也可以像struct那样用impl实现,并且定义方法 impl Message { fn call(&self) { // 在这里定义方法体 } } // 像struct一样去使用 m.call(); }
Option
https://doc.rust-lang.org/std/option/enum.Option.html Option的文档,提供了方法
#![allow(unused)] fn main() { enum Option<T> { //<T> 语法是一个我们还未讲到的 Rust 功能。它是一个泛型类型参数 None, Some(T), } // 不需要 Option:: 前缀来直接使用 Some 和 None // Some(T) 和 None 仍是 Option<T> 的成员 let some_number = Some(5); let some_string = Some("a string"); let absent_number: Option<i32> = None; }
匹配match
#![allow(unused)] fn main() { // 匹配枚举类型的值 fn value_in_cents(coin: Coin) -> u8 { match coin { Coin::Penny => 1, Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter(state) => { println!("State quarter from {:?}!", state); 25 } } } // 匹配Option fn plus_one(x: Option<i32>) -> Option<i32> { match x { None => None, Some(i) => Some(i + 1), } } // 匹配值以外的其他值 match dice_roll { 3 => add_fancy_hat(), 7 => remove_fancy_hat(), // 这里other是可以拿到这个值的,并在move_player函数中使用 other => move_player(other), //如果不想要这个值,直接执行一个行数 // _ => reroll(), //也可以什么都不要,什么都不执行 //_ => (), } }
if let
#![allow(unused)] fn main() { let config_max = Some(3u8); // 匹配一个值 if let Some(max) = config_max { println!("The maximum is configured to be {}", max); } // 或者 let mut count = 0; // 匹配俩个值 if let Coin::Quarter(state) = coin { println!("State quarter from {:?}!", state); } else { count += 1; } }
错误处理
示例代码
// 这是一个package的开始 use std::fs::File; use std::io; use std::io::Read; fn main() { panic!("抛出了一个错误"); //抛出一个错误,程序中断 // enum Result<T, E> { // Ok(T), // Err(E), // } let f = File::open("hello.txt"); //返回一个Result的enums类型 let _ff = match f { // 没有被使用的变量可以加上下划线前缀 _ff ff没有被使用 Ok(_file) => println!("exits"), Err(error) => panic!("Problem opening the file: {:?}", error), }; // unwrap() expect(string) 俩个函数都是如果发生错误就直接panic! 否则返回正确的值,expect则允许传一个字符串作为错误提示 let _f = File::open("hello.txt").unwrap(); let _f = File::open("1.txt").expect("出错了"); let _g = open_file(); }
直接抛出错误
- panic!(“出错了”) - 直接抛出一个错误后,程序中断
- unwrap() - 对于返回类型是Result的结果,返回错误的话就直接panic!了,返回正确的话就得到正确的值
- expect(“出错了”) - 和unwrap一个,区别在于可以传递一个自定义的错误信息
函数返回错误
#![allow(unused)] fn main() { fn open_file() -> Result<String, io::Error> { let mut s = String::new(); File::open("1.txt")?.read_to_string(&mut s)?; // 发生错误的话,直接return错误了 Ok(s) // 返回正确的值 } }
- 函数返回结果是Result类型的话,可以直接使用?来直接return err,错误信息会传递给Result.
- 这里最后一行结果会默认return给Result
trait
trait限定
Where关键字
fn foo<T: A, K: A + C, B> (a: T, b: K, c: B) {}
优化为:
fn foo<T, K, B> (a: T, b: K, c: B) where T: A, K: A + C {}
where 关键字 用来给泛型T增加trait限定
项目实践篇
description: 自定义一个crate,并发布到crates.io,然后在项目中引用crate
自定义包并发布
1、首先得有个包
创建一个crate库
#![allow(unused)] fn main() { // 创建一个业务代码常用的自定义方法库businessfn cargo new --lib businessfn }
定义模块以及函数
目录结构如下:
businessfn:
----src
---------arr.rs
---------lib.rs
----.gitignore
----Cargo.lock
----Cargo.toml
----README.md
arr.rs:
/// 获取二维数组的列,返回一个包含该列所有值的一维数组 /// /// # Examples /// /// ``` /// use businessfn::hello; /// /// fn main() { /// hello(); /// } /// ``` pub fn array_columns() -> i64 { return 1 }
lib.rs:
#![allow(unused)] fn main() { /// 业务函数库 pub mod arr; // 加载arr同名文件的模块 pub fn hello() { let i = arr::array_columns(); println!("i num : {}", i); } }
Cargo.toml:
#![allow(unused)] fn main() { [package] name = "businessfn" description = "Some custom function libraries for business development" documentation = "https://github.com/finewenmu/businessfn/blob/master/README.md" homepage="https://github.com/finewenmu/businessfn" version = "0.1.0" license = "MIT" edition = "2021" repository = "https://github.com/finewenmu/businessfn" readme = "README.md" See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] }
编译发布版本
#![allow(unused)] fn main() { Cargo build --release }
2、其次得做些准备
注册一个github账号并且创建一个businessfn的仓库
注册github账号,点击[走你](https://github.com),自行注册即可,若有账号,继续下一步
创建github公开仓库,自行百度,不再赘述
然后你就有一个类似这样的仓库了 :https://github.com/finewenmu/businessfn
使用ssh-keygen生成本地秘钥。然后配置到github->setting->ssh设置中
这一步也不再细说,自行百度ssh-keygen -t rsa -c "xxx@gmail.com"
在businessfn中绑定你的仓库,并将代码push到仓库中
这一步也不再细说,自行百度git的使用
现在取crates.io注册一下账号,这个账号目前只能是用github授权的方式绑定
创建API token
注册授权登录之后,在个人中心->Account setting->Api Token页面,点击New Token即可生成
3、发布自定义库到Crates.io
本地登录Crates.io账号
打开终端执行命令:
cargo login [token]
// 比如
cargo login xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//提示登录成功
在项目businessfn根目录执行发布
$cargo publish
Updating crates.io index
Packaging businessfn v0.1.0
Verifying businessfn v0.1.0
Compiling businessfn v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 2.06s
Uploading businessfn v0.1.0
4、使用自定义库
创建一个项目
#![allow(unused)] fn main() { cargo new businessfn_test }
在Cargo.toml添加依赖
#![allow(unused)] fn main() { [dependencies] businessfn = "0.1.0" }
在main.rs中使用
use businessfn::hello; fn main() { hello(); }
运行
$cargo run
Updating crates.io index
Compiling businessfn v0.1.0
Compiling businessfn_test v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 5.52s
Running `target\debug\businessfn_test.exe`
i num : 1
结束
恭喜!你成功了!庆贺吧!去搓一顿吧!!!
businessfn库
http库
https://hyper.rs/ 用于 Rust 语言的快速且安全的 HTTP
附录
一些函数
dbg!
dbg!宏会打印到标准错误控制台流(stderr),与println!不同,后者会打印到标准输出控制台流(stdout)。
fn main() { let scale = 2; let rect1 = Rectangle { width: dbg!(30 * scale), // 打印stderr日志,并且返回计算结果 height: 50, }; dbg!(&rect1); // 打印stderr日志 }
assert_eq!
#![allow(unused)] fn main() { assert_eq!(maybe_some_len, Some(12)); //相等不输出 不相等输出错误信息 }
格式化输出
{}
输出标准数据类型
{:?}
输出复合数据类型
{:#?}
复合类型的格式化输出
知识碎片
Rust
- 语法参考
- cargo管理工具
- rustc编译器
- 标准库
- crate.io库管理
一些问题
{} 和 {:?} 的区别
println!("x is {}", x); // {}指的是任意变量内容 println!("y is {:?}", y);//fmt::Debug:使用 {:?} 标记。格式化文本以供调试使用。{:#?}提供了 “美化打印” 的功能//fmt::Display:使用 {} 标记。以更优雅和友好的风格来格式化文本。
代码块{}的最后表达式加不加分号“;”的区别
let y = { let x_squared = x * x; let x_cube = x_squared * x; // 将此表达式赋给 `y` x_cube + x_squared + x};let z = { // 分号结束了这个表达式,于是将 `()` 赋给 `z` 2 * x;};
给变量赋值()是什么意思
//单元类型(unit type):()。其唯一可能的值就是 () 这个空元组let z = ()
println!() 为啥要加个!号
//println! 是一个宏(macros),可以将文本输出到控制台(console)
Rust的代码文件是以.rs结尾的
$rustc hello.rs
Rust的注释
//rust的注释是// 或者 /*块注释*/
Rust的文档注释
//rust的注释是/// 或者//!
struct Structure(i32) 定义一个结构体
struct Structure(i32) //定义一个元组结构体,包含一个 `i32` 元素// 元组结构体struct Pair(i32, f32);
#![crate_attribute] 是啥意思
#![crate_attribute] //作用在crate的属性#[item_attribute] //作用在模块、项的属性
//属性的格式#[attribute = "value"]#[attribute(key = "value")]#[attribute(value)]
#[allow(dead_code)]啥意思
//死代码,不被调用的代码#[allow(dead_code)]fn unused_function() {}
\
错误检查
error: a bin target must be available for cargo run
cargo run 会找
src/main.rs或src/bin/*.rs去运行,如果没有这些文文件,只有src/lib.rs,那么可以执行cargo test单元测试即可
国内镜像
修改~/.cargo/config 文件,如果没有就创建
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'tuna'
[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"
#replace-with = 'ustc'
#[source.ustc]
#registry = "git://mirrors.ustc.edu.cn/crates.io-index"