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-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
archisizeusize

浮点

f32f64

布尔型

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" %}

  1. 表达式结果会直接给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: 待补充

所有权

所有权

  1. Rust 中的每一个值都有一个被称为其 所有者owner)的变量。
  2. 值在任一时刻有且只有一个所有者。
  3. 当所有者(变量)离开作用域,这个值将被丢弃。

实现了copy trait的类型,不会转移所有权

  • 所有整数类型,比如 u32
  • 布尔类型,bool,它的值是 truefalse
  • 所有浮点数类型,比如 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授权的方式绑定

地址:https://crates.io/

创建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.rssrc/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"