Featured image of post RUST 学习记录

RUST 学习记录

分享学习RUST的过程(不保证完全准确)

RUST 的数据类型

整数类型

无符号整数

类型 范围
u8 0~28-1
u16 0~216-1
u32 0~232-1
u64 0~264-1
u128 0~2128-1
usize 0~232-1 or 0-264-1

usize取决于系统位数,32为32,64为64.

有符号整数

类型 范围
i8 -27~27-1
i16 -215~215-1
i32 -231~231-1
i64 -263~263-1
i128 -2127~2127-1
isize -231~231-1 or -263~263-1

isize同上取决于系统位数,32为31,64为63.

浮点类型

类型 精度 范围
f32 IEEE 单精度(至少6位小数) 约-3.4×1038~3.4×1038
f64 IEEE 双精度(至少15位小数) 约-1.8×10308~1.8×10308

布尔类型

  • True & False

字符类型

  • Rust的字符类型以32位值的形式表示单个Unicode字符
    • 这使得它可以支持i18n甚至包括emoji

数组

  • 最简单的形式,将数据直接括起来:[1,1,2,3,5,8,13]
  • *不要把数组局限于数;如["hello", "rust", "love", "you"]
    也是一个合法的数组
  • 可以通过[V; N]来生成一个相同元素的数组;
    • 其中,V为每个元素的值[Value], N为数据个数[Number].

元组

  • 元组不同于数组,元组中的元素可以是任何类型
    例如("rust", 520)是一个元组,它的类型是(&str, i32)
  • 访问元组中元素的方法tup.0, tup.1, tup.2
    index从0开始
  • *零元组:()

指针类型

引用

&xx类型的值就是对一个xx值的引用

&T : 不可变引用;
&mut T : 可变引用.

Box

1
2
let tup_1 = (12, "eggs");
let box_1 = Box:new(tup_1); //在堆中分配一个元组
  • box分配的元组在堆内存中,即涉及到所有权

原始指针

  • *mut T
  • *const T

    星号[*]还可以表示解引用

向量

向量Vec<T>分配在堆内存上[可变]

1
let mut vec_1 = vec![2, 3, 5, 7];
  • 以上使用了vec!宏创建向量
1
2
vec_1.push(11);
vec_1.push(13);
  • 以上使用push()vec_1向量中添加元素

切片

以下先创建数组与向量

1
2
let vec_1: Vec<f64> = vec![0.0,  0.707,  1.0,  0.707];
let arr_1: [f64; 4] =     [0.0, -0.707, -1.0, -0.707];

首先来介绍普通引用

1
2
let sv: &[f64] = &vec_1;
let sa: &[f64] = &arr_1;

以上创建了不可变引用
sv和sa的指针指向源数据而不是创建了新的数组和向量
此时svsa输出结果和vec_1arr_1相同

若想获得特定的元素或者元素片段,则需要进行切片

切片

可以使用如下语句来将数组或者向量切片

1
2
3
print(&vec_1[0..2]); //打印vec_1的前两个元素
print(&arr_1[2..]); //从a[2]开始打印arr_1的元素
print(&sv[1..3]); //这是有趣的一部分,因为它对一个切片进行了切片!

字符串

字符串字面量

字符串字面量放置于双引号中;例如"hello rust!" 这种形式的数据存储于栈内存中,因此无需考虑所有权机制.

字符串

以下方式创建了字符串(String);

1
let str_1 = "hello rust".to_string();

需要注意的是,字符串(String)存储在堆内存中,因此在使用时需要考虑所有权机制!
下一章将介绍所有权机制!

所有权

rust通过所有权机制而不是垃圾回收机制来确保内存安全

转移

在rust中,给变量赋值的操作通常不会创建新值,而是将新的指针指向旧值(栈数据除外)

1
2
let v_1 = "Hello rust".to_string();
let v_2 = v_1;

上述代码创建了两个变量为v_1v_2并且他们的值都为Hello rust,在内存中,这两个变量指向的是同一个地址,同一个数据.

同理,为函数传递参数也是这种结果.

值得注意的是,存储在栈内存中的数据在传参等操作后,原值通常可用(这也称作copy,也就是会产生新的数据.),而存储在堆内存中的数据经过这样的操作后原值会变为未初始化的状态.

1
2
3
4
5
6
7
let x = vec![10 20 30];
if c {
  f(x); //传入x
} else {
  g(x); //同上
}
h(x) //Error!,因为x此时是未初始化状态

引用

rust有一种不获取源数据所有权来调用源数据内容的方式叫做引用 可以使用&e来获得一个对e的引用,和rust的变量部分一样,引用通常也是不可变的,如果要获得可变引用,则需要&mut e

  • rust不允许同时存在可变引用和不可变引用,但是允许同时存在多个不可变引用.
  • rust也允许对引用进行引用,例如
  • 1
    2
    3
    4
    
    struct Points {x: i32, y: i32}
    let point = Points {x: 1000, y: 2000};
    let r: &Points = &point;
    let rr: &&Points = &r;
    

    如上给定了数据类型,但是rust也可以自行推断使用了几层引用.

  • 不允许对生命周期已经结束的数据进行引用例如
  • 1
    2
    3
    4
    5
    6
    7
    
    fn main() {
      {
        let v_1 = 42;
      }
    
      println!(&v_1); //此处非法,因为已经离开了作用域,v_1已经被释放!
    }
    

表达式

表达式语言

在rust中,if和match可以产生值
在c语言中,流程控制工具大多数是语句,而在rust都是表达式
Rust 的 if 表达式可以用来初始化变量

1
2
3
4
5
6
let status =
    if cpu.temp <= MAX_TEMP {
    HttpStatus::Ok
} else {
    HttpStatus::Error
};

Rust 的 match 表达式可以作为参数传给函数或宏

1
2
3
4
5
println!("Inside the vat, you see {}.",
    match vat.contents {
        Some(brain) => brain.decs(),
        None => "nothing of interest"
    });

这就解释了Rust为什么不需要类似C语言的那种三元操作符

块与分号

代码块同时也是表达式,代码块的值就是最后一个表达式的值

1
2
3
4
5
6
7
8
let display_name = match post.author() {
    Some(author) => author.name(),
    None => {
        let network_info = post.get_network_netadata()?;
        let ip = network_info.client_address();
        ip.to_string()
    }
};

None =>之后的部分是一个块表达式,这里块的值就是最后一个表达式ip.to_string()的值

声明

通常用let来声明局部变量

1
let name: type = expr;

其中,类型和初始值是可选的(Rust会自动进行类型推断),而分号是必须的.

Rust 中,变量默认不可变,例如下面这段代码是会报错的

1
2
let value_1 = 5;
value_1 = 2;

如需声明可变变量,则需要使用let mut

1
2
let mut value_1 = 5;
value_1 = 2;

if 与 match

if很简单,如下

1
2
3
4
5
6
7
if condition1 {
    block1
} else if {
    block2
} else {
    block3
}

这里不再过多介绍,接下来是match
match有点像C语言的switch,但是更灵活;

1
2
3
4
5
6
match code {
    0 => println!("Ok"),
    1 => println!("Wires Tangled"),
    2 => println!("User Asleep"),
    _ => println!("Unrecognized error {}", code)
}

根据code的值,四个分支只有一个会执行,其中_表示任何值;

  • 值得注意的是,match的分支是从第一条匹配到最后一条,也就是说如果将第四条放在顶端那么这段代码任意code都会输出Error

if let 表达式

if let表达式只是对match表达式的简写,if let可以实现的功能完全可以由match实现

1
2
3
4
5
if let pattern = expr {
    block1
} else {
    block2
}

这个结构实现的效果是:expr要么匹配pattern,运行block1,要么不匹配pattern,运行block2

循环

Rust中有四种循环实现方式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
while condition {
    block
}

while let pattern = expr {
    block
}

loop {
    block
}

for pattern in collection {
    block
}

循环在rust中也是表达式但是循环产生的值是()

  • while循环和C类似
  • while let循环和if let类似,要么匹配后运行后面的代码,要么不匹配后退出循环
  • loop是死循环,除非遇到retrun或者break或者程序挂了
  • for循环,很简单,遍历.
  • 一般的,进行有限次循环时通常采用for遍历数组的形式,而不是loop或者while
    1
    2
    3
    
    for i in 0..20 {
        println!("{i}");
    }
    

return 表达式

return表达式退出当前函数,并且向调用者返回值. 通常使用return;来代表return ();

函数与方法调用

一般地,有以下规则

1
2
3
let x = gcd(1302, 462); //函数调用

let room = player.location(); //方法调用

同时,还有对静态方法的调用

1
let mut numbers = Vec::new(); //静态方法调用
  • 静态方法与非静态方法的区别是静态方法通过类型调用,而非静态方法通过值调用
  • 方法也可以链式调用
    1
    
    Iron::new(router).http("localhost:3000").unwarp();
    
  • 需要注意的是,通常用于函数调用或方法调用的语法不能用于泛型Vec 所以需要使用::<T>,而不是<T>:例如
    1
    
    return Vec::<i32>::with_capacily(1000);
    

字段与元素

1
2
3
game.black_pawns //结构体字段
coords.1         //元组元素
pieces[1]        //数组元素

此处还涉及到切片,我们以上提到过这里不再赘述
需要注意的是,在使用a..b这种结构来表示切片范围时,Rust会将其理解为一个半闭半开区如

1
2
3
0..5 
0 <= i < 5 
[0, 5)

引用操作符

& &mut在之前介绍过,这里依然不再重复

Licensed under CC BY-NC-SA 4.0
发表了2篇文章 · 总计4.26k字
使用 Hugo 构建 · 主题 Stack 由 Jimmy 设计