Rust学习笔记

2023-11-18

Rust学习笔记

文章目录

1.0 Rust概述

最近因为接触到一个有趣的前端桌面应用新方案tauri,由于tauri的后端是rust,因此提起了我对rust的兴趣。

Rust语言的特点

  • 高性能 - Rust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收,它能够胜任对性能要求特别高的服务,可以在嵌入式设备上运行,还能轻松和其他语言集成。
  • 可靠性 - Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全,让您在编译期就能够消除各种各样的错误。
  • 生产力 - Rust 拥有出色的文档、友好的编译器和清晰的错误提示信息, 还集成了一流的工具 —— 包管理器和构建工具, 智能地自动补全和类型检验的多编辑器支持, 以及自动格式化代码等等。

rust适合的领域

  • 网络编程
  • 游戏编程
  • web后端与wasm
  • 命令行工具
  • 操作系统
  • 嵌入式

1.1 安装、配置开发环境

安装rust

这里不过多赘述。

linux

curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

如果安装成功,将出现下面这行:

Rust is installed now. Great!

windows

rust编译工具:https://www.rust-lang.org/zh-CN/tools/install)

Windows 上安装 Rust 需要有 C++ 环境,有两种方式可供选择:

  1. x86_64-pc-windows-msvc(官方推荐)
  2. x86_64-pc-windows-gnu

在安装时可自行选择。

配置开发环境

推荐vscode作为rust的开发工具

Visual Studio Code下载地址:https://code.visualstudio.com/Download

插件:

  • rust-analyzer(必装)
  • Better TOML(推荐)
  • crates(推荐)
  • Error Lens(推荐)

1.2 cargo

Cargo 是什么

Cargo 是 Rust 的构建系统和包管理器。

Rust 开发者常用 Cargo 来管理 Rust 工程和获取工程所依赖的库。

cargo常用指令

  • cargo build 构建你的项目
  • cargo run 运行你的项目
  • cargo test 测试你的项目
  • cargo doc 构建你的项目的文档
  • cargo publish 发布你的库至crates.io

快速开始

cargo new [项目名]

修改国内源

[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
# 指定镜像
replace-with = '镜像源名' # 如:tuna、sjtu、ustc,或者 rustcc 
# 注:以下源配置一个即可,无需全部 
# 中国科学技术大学
[source.ustc]
registry = "https://mirrors.ustc.edu.cn/crates.io-index"
# >>> 或者 <<<
registry = "git://mirrors.ustc.edu.cn/crates.io-index" 
# 上海交通大学
[source.sjtu]
registry = "https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index/" 

# 清华大学
[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git" 

# rustcc社区
[source.rustcc]
registry = "https://code.aliyun.com/rustcc/crates.io-index.git"

添加包(依赖)

比如添加一个随机数的包

只需去crates.io搜索rand

image-20220707171603881

将其添加至Cargo.toml中的[dependencies]

image-20220707172103287

2.1 rust变量和基础数据类型

变量

变量是什么

  • 将数据分配到临时内存位置,为了让程序员更好地操作内存
  • 可以被赋值于任何的值和类型
  • rust中let声明变量
  • rust中的变量默认是不可变的!,前置 mut可以使其为可变的
    • Immutable
    • Mutable

输入一个字符串并输出,main.rs:

// 标准库io包
use std::io;
fn main() {
    let mut input: String = String::new();
    println!("Hello, world!");
    match io::stdin().read_line(&mut input) {
        Ok(_) => {
            println!("Wow : {}", input);
        }
        _ => {}
    }
}

命令行执行cargo run

rust 基本数据类型

Integer 整型

Size Signed Unsigned
8 bit i8 u8
16 bit i16 u16
32 bit i32 u32
64 bit i64 u64
128 bit i128 u128
arch(系统架构有关) isize usize

Float 浮点型

Size Float
32 bit f32
64 bit f64

Boolean 布尔型

true
false

Character 字符类型

单引号

let a = 'a';
let smile = '\u{1f601}';

&str 字符串类型

双引号

let cat: &str = "car";

tips:若不写类型也会自动推导,如下:

image-20220707164443315

!表示宏

println!:打印信息到终端

有关于宏,我会在后续高级教程中再去写一遍,宏是rust中比较复杂的一个东西,大致分为两类:声明式宏( declarative macros ) macro_rules! 和三种过程宏( procedural macros ):

  • #[derive],在之前多次见到的派生宏,可以为目标结构体或枚举派生指定的代码,例如 Debug 特质
  • 类属性宏(Attribute-like macro),用于为目标添加自定义的属性
  • 类函数宏(Function-like macro),看上去就像是函数调用

2.2 控制流(control flow)

Execution Flow

语句

if
else if
else

循环

loopingiteration

关键字:

  • loop无尽的循环

  • while有条件的循环

  • for

  • break

  • continue

loop and while

因为loop是无尽的循环,所以需要通过break退出:

image-20220707170928433

for

fn main() {
    for 元素 in 集合 {
      // 使用元素
    }
}
使用方法 等价使用方式 所有权
for item in collection for item in IntoIterator::into_iter(collection) 转移所有权
for item in &collection for item in collection.iter() 不可变借用
for item in &mut collection for item in collection.iter_mut() 可变借用

Match

if...else类似,但是必须写出所有条件下的执行代码

if...else不同的是:

  • match会在编译时检查错误
  • if...else则不会,只会在运行时报错
use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();
    let num = rng.gen_range(0..10);
    match num {
        1 => println!("first"),
        2 => println!("second"),
        3 => println!("third"),
        _ => println!("other"),
    }
}

可以用下划线_表示其它所有情况

tips:在 Rust 中 _ 的含义是忽略该值或者类型的意思,如果不使用 _,那么编译器会给你一个 变量未使用的 的警告。

2.3 函数

普通函数

从之前的示例代码可以看出来,fn就是定义函数的关键字

fn function(a:i32, b:i32) -> i32 {
    a + b
}

tips:最后一行不加;自动作为返回值返回

Closure 闭包

  • 函数中的函数
  • 匿名函数
  • lambda表达式
fn main() {
    let add = |a: i32, b: i32| -> i32 { a + b };
    let res = add(1, 2);
    println!("{}", res);
}

3.1 枚举 Enum

枚举可以是多个不同的枚举类型之一

主要作用:

  • 提高代码的可读性
  • 提供标识给编译器(提高性能)

系统的枚举

  • Result
  • Option

代码

// 便于打印
#[derive(Debug)]
enum Position {
    One,
    Two,
}
fn main() {
    // 打印复合类型
    println!("{:?}", Position::One);
}

3.2 结构体

  • rust中没有class

struct

  • 包含多段数据的类型
  • 每一段数据被称为field属性
  • 访问属性用.
#[derive(Debug)]
struct Rectangle {
    width: i32,
    height: i32,
}
fn main() {
    let rect = Rectangle {
        width: 5,
        height: 10,
    };
    println!("{}", rect.width);
    println!("{:#?}", rect);
}

struct 方法

  • 关联函数
  • 实例方法
  • 构造函数
  • Self

示例1

#[derive(Debug)]
struct Rectangle {
    width: i32,
    height: i32,
}

impl Rectangle {
    // 关联函数
    fn area(width: i32, height: i32) -> i32 {
        width * height
    }
    // 实例方法
    fn cal(&self) -> i32 {
        self.width * self.height
    }
    // 构造函数
    fn new(width: i32, height: i32) -> Self {
        Rectangle { width, height }
    }
}

fn main() {
    let rect = Rectangle {
        width: 5,
        height: 10,
    };
    let num1 = Rectangle::area(5, 5);
    println!("{}", num1);
    println!("{}", rect.cal());
    let rect2 = Rectangle::new(5, 5);
    println!("{:#?}", rect2);
}

示例2

#[derive(Debug)]
struct Person {
    name: String,
    age: i32,
}

impl Person {
    // 可能在外部调用,使用pub关键字
    pub fn new(name: String, age: i32) -> Self {
        Person { name, age }
    }
}

fn main() {
    let person1 = Person::new("Star-tears".to_string(), 18);
    println!("{:#?}", person1);
}

  • &self
  • &mut self
  • self
  • mut self

示例3

#[derive(Debug)]
struct Person {
    name: String,
    age: i32,
}

impl Person {
    // 可能在外部调用,使用pub关键字
    pub fn new(name: String, age: i32) -> Self {
        Person { name, age }
    }
    fn greet(&self) -> String {
        format!("Hi {}", self.name)
    }
    fn add_age(&mut self, n: i32) {
        self.age += n;
    }
}

fn main() {
    let mut person1 = Person::new("Star-tears".to_string(), 18);
    println!("{:#?}", person1);
    println!("{}", person1.greet());
    person1.add_age(3);
    println!("{}", person1.age);
}

&为引用,但默认是不可变的,如果希望可变,需要加mut关键字

3.3 Tuples 元组

  • 相似类型有数组、切片,但在rust中不常用,常用Vector

元组

  • 匿名存储数据
  • 不可变

元组的用处

  • 函数返回
  • 提取变量

代码

fn a_tuple() -> (i32, i32) {
    (0, 1)
}

fn main() {
    println!("{:?}", a_tuple());
    let (a, b) = a_tuple();
    println!("a: {}, b: {}", a, b);
}

4.1 所有权机制(OwnerShip)

OwnerShip Model

OwnerShip规则

  • 每个值都有一个变量称为所有者
  • 每个值只能有一个所有者
  • 当所有者超出作用域时,值被销毁

Stack 和 Heap

  • Stack存储已知大小的数据 快 基础数据类型等
  • Heap存储未知大小的数据 慢 Struct等

move

浅拷贝加失效

fn main() {
    let a = "abc".to_string();
    let b = a; // a失效
}

其它语言在这样子的赋值时大多数只做一个浅拷贝,但在rust里,它是把所有权交给b,将a失效

clone

  • 深度拷贝
  • 开辟新的空间
fn main() {
    let a = "abc";
    let b = a;
    println!("{}, {}", a, b);
}

输出如下:

    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target\debug\hello.exe`
abc, abc

引用

reference

  • 通过&可以获得值的引用
  • 未获得值的所有权,那么它就不会在作用域结束后被销毁
  • 这种情况叫借用borrowed
struct A {
    a: i32,
}
fn change_a(a: &mut A) {
    a.a = 233;
    println!("{}", a.a);
}

fn main() {
    let mut a = A { a: 1 };
    println!("{}", a.a);
    change_a(&mut a);
    println!("{}", a.a);
}

注意

在同一作用域下,对某一块数据:

  1. 可以有多个不可变引用
  2. 只能有一个可变的引用
  3. 不能同时拥有一个可变引用和一个不可变引用

Copy 和 Clone

这两个都是特质(trait)

  • Copy trait
  • Clone trait
#[derive(Debug, Clone, Copy)]
struct Person<'a> {
    name: &'a str,
    age: i32,
}

fn main() {
    let person = Person {
        name: "Star-tears",
        age: 18,
    };
    let pp = person;
    println!("{:?}", person);
}

tips:手动标注name作用域与结构体实例作用域一致

4.2 生命周期 lifetime

生命周期

  • 避免dangling referfence(避免悬垂引用)
  • rust中所有的引用都有自己的生命周期,表示引用有效的作用域
  • 一般为隐式的,但不可推断时会报错,需要手动标注生命周期

手动标识生命周期

  • fn longest(x: &str, y: &str) -> &str
    
  • 生命周期最短的为有效

  • 尽量不要用'static

  • 省略规则

    • 每个引用类型的参数都有自己的生命周期
    • 只有一个输入生命周期参数,那么输出生命周期就为该生命周期
    • &self&mut self,那么输出生命周期就为该self生命周期
  • 生命周期机制还在完善

5.1 常用数据类型

String

  • &str: String slices 不可变的

    • let ss = "ss";
      
  • String objects

    • let mut s = String::new();
      
    • let s = String::from("star");
      
    • let s =  "s".to_string();
      
    • 通过format!生成

      let s = format!("{}", 233);
      

String methods

  • len()
  • push 字符
  • push_str 字符串
  • replace

Vector

构造

let mut arr = Vec::new();
let arr = vec![1,2,3];
let arr = vec![1;20];

push 和 remove

arr.push(2);
arr.remove(0);

更新

num[3] = 5;
num.get(3)
fn main() {
    let mut num: Vec<i32> = vec![0; 20];
    num.push(1);
    match num.get(20) {
        Some(n) => println!("{}", n),
        None => {}
    }
}

遍历

  • iter
fn main() {
    let mut num: Vec<i32> = vec![0; 20];
    num.push(1);
    for it in num.iter() {
        println!("{}", it);
    }
}

5.2 HashMap

  • 散列函数

    • 散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫散列值(hash values, hash codes, hash sums, 或hashes)的指纹
  • HashMap由链表加数组组成,它的底层结构是一个数组,而数组的元素是一个单向链表

  • HashMap 的键可以是布尔型、整型、字符串,或者任意实现了 Eq 和 Hash trait 的其他类型

  • HashMap 也是可增长的,但 HashMap 在占据了多余空间时还可以缩小自己

use std::collections::HashMap;

fn main() {
    let mut str_map = HashMap::new();
    str_map.insert("star", 18);
    str_map.insert("tears", 19);
    println!("{:#?}", str_map);
    match str_map.get(&"star") {
        Some(v) => println!("{:#?}", v),
        _ => {}
    }
}

6.1 特质和泛型(Trait && generics)

Traits

  • 与接口和抽象类类似
  • 给结构体添加定义的行为
impl trait_demo for struct_demo
struct Steve {
    name: String,
}

impl Person for Steve {
    fn new(name_str: String) -> Self {
        Steve { name: name_str }
    }

    fn language(&self) -> &str {
        "Steve"
    }
}

struct Alex {
    name: String,
}

impl Person for Alex {
    fn new(name_str: String) -> Self {
        Alex { name: name_str }
    }

    fn language(&self) -> &str {
        "Alex"
    }
    fn eat_food(&self) {
        println!("Eat fish!");
    }
}

trait Person {
    fn new(name_str: String) -> Self;
    fn language(&self) -> &str;
    fn eat_food(&self) {
        println!("Eat food");
    }
}

fn main() {
    let a = Steve::new("Star_tears".to_string());
    let b = Alex::new("T_dream".to_string());
    a.eat_food();
    b.eat_food();
}

  • 返回枚举

    • fn get_trait -> Box<dyn trait_name>
      

泛型

  • 泛型程序设计(generic programming)是程序设计语言的一种风格或范式
  • 泛型运行程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型
trait Bark {
    fn bark(&self) -> String;
}

struct Dog {
    species: String,
}

struct Cat {
    color: String,
}

impl Bark for Dog {
    fn bark(&self) -> String {
        format!("{} barking", self.species)
    }
}

fn bark<T: Bark>(b: T) {
    println!("{}", b.bark());
}

fn main() {
    let dog = Dog {
        species: "white dog".to_string(),
    };
    bark(dog);
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Rust学习笔记 的相关文章

随机推荐

  • AC自动机 (多模式匹配)

    AC自动机 感谢博主 https blog csdn net bestsort article details 82947639 感谢博主 https fanfansann blog csdn net article details 106
  • C++socket编程(三):3.5 accept读取用户的连接信息

    读取用户的连接信息 顾名思义 就是在服务段中获取连接进来的客户端的ip地址 套接字编号 ip地址 端口号等 下面开看代码 获取用户客户端的socket号 int client accept sock 0 0 创建一个新的socket 用来与
  • 软件测试笔记(九)- 兼容性测试

    了解如何针对不同的软件应用程序和操作系统交互的问题进行测试 一 兼容性测试综述 随着用户对来自各个厂商的各种类型程序之间共商数据能力和充分利用空间同时执行多个程序能力的要求 测试程序支架能否写作变得越来越重要 软件兼容性测试 softwar
  • Qt使用msvc编译器情况下,如何进行内存泄漏检测

    背景 使用Qt5版本 编译器选择msvc2017 在测试基于tinyxml2的二次封装类接口是否存在内存泄漏问题时 寻找内存泄漏检测工具 问题 寻找适合Qt msvc编程的内存泄漏检测工具 尝试 VLD Visual Leak Detect
  • 【Linux】基础I/O

    在C语言中我们学习到了文件的I O 可以回顾一下 https blog csdn net mmwwxx123 article details 81516082 系统文件I O linux下所有设备都是以文件存在的 可以说是一切皆文件 所以当
  • dc综合报告wand,vc spyglass lint工具不报告W145多驱动。原因是什么?

    因为vc spyglass lint对unload的信号 会不报告W145多驱动 另外 dc工具 会在某层次 认为1 b0是n1487信号线 dc check design报告里 会提醒多驱动 连接到了n1487信号线上 解决方法 根据dc
  • 【C++/STL】手撕AVL树

    文章目录 1 map中的问题 1 1map的insert 函数剖析 1 2map对 的重载 2 AVL树的模拟实现 2 1AVL树的概念 2 2AVL树节点的定义 2 3AVL树的插入 1 在较高的右子树右侧插入数据 左单旋 2 在较高的左
  • 【操作系统】寄存器

    概念 寄存器是CPU内部用来存放数据的一些小型存储区域 用来暂时存放参与运算的数据和运算结果 其实寄存器就是一种常用的时序逻辑电路 但这种时序逻辑电路只包含存储电路 寄存器的存储电路是由锁存器或触发器构成的 因为一个锁存器或触发器能存储1位
  • 443M衣架遥控arduino代码备档

    byte up 65 0 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1
  • 机器学习笔记GBDT(一):原理

    目录 文章目录 目录 前言 1 GBDT概述 2 GBDT的负梯度拟合 3 GBDT回归算法 1 初始化弱学习器 2 对于迭代轮数t 1 2 T有 3 得到强学习器f x 的表达式 4 GBDT分类算法 4 1 二元GBDT分类算法 4 2
  • Spring Boot自动扫描

    进行Spring Boot和Mybatis进行整合的时候 Spring Boot注解扫描的时候无法扫描到Application类的以外的包下面的注解 如下图 App就是Application类 下图是ProductMapper 类 Mapp
  • 函数的参数(形参与实参)的理解

    函数的参数 实际参数 实参 真实传给函数的参数 叫实参 实参可以是 常量 变量 表达式 函数等 无论实参是何种类型的量 在进行函数调用时 它们必须有确定的值 以便把这些值传送给形参 形式参数 形参 形式参数是指函数名后括号中的变量 因为形式
  • springboot项目响应信息Jackson解析映射,key为null时抛异常解决办法

    当使用 RestController注解时 会把响应信息自动解析成json格式 使用的是Jackson 但是Jackson默认不解析key为null的映射时会抛出异常 需要增加配置 解决 import com fasterxml jacks
  • 执行npm run dev 报【<--- Last few GCs --->内存溢出】

    setx NODE OPTIONS max old space size 10240 cmd 运行 set NODE OPTIONS max old space size 4096 这两个都试过都不行 欲哭无泪 后来听大佬说要下载 npm
  • yum与apt的区别

    一般来说著名的 Linux 系统基本上分两大类 RedHat 系列 Redhat Centos Fedora 等 Debian 系列 Debian Ubuntu 等 对比项 rpm yum dpkg apt 系列 RedHat系 RedHa
  • 什么是加密(Encrypt)?什么是哈希(Hash)?

    加密 Encrypt 加密的概念 假设有一个参数k和一种变换方式E 原始信息 m 通过变换 E 得到一个新的字符串c 公式为 c E m 那么我们就称原始信息 m 为明文 新字符串 c 为密文 将明文转化为密文的过程叫做加密 E 这种变换方
  • Your password has expired. To log in you must change it using a client that supports expired pa

    这个是初始密码问题 有两张方法 我这里用的是命令行的方法 即进入相应mysql目录 再修改密码的方法 首先输入mysqld defaults file F Program Files x86 MySQL my ini skip grant
  • std::bind

    std bind是函数模板 是一个函数 使用std bind可以将可调用对象和参数一起绑定 绑定后的结果使用std function进行保存 并延迟调用到任何我们需要的时候 std bind返回一个基于f的函数对象 其参数被绑定到args上
  • 一个老程序员告诉你:中国程序员为什么要跳槽

    http www jizhuomi com career 318 html
  • Rust学习笔记

    Rust学习笔记 文章目录 Rust学习笔记 1 0 Rust概述 Rust语言的特点 rust适合的领域 1 1 安装 配置开发环境 安装rust linux windows 配置开发环境 1 2 cargo Cargo 是什么 carg