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"