Rust 数据类型

Rust 数据类型

简介

Rust 的数据分为 标量(scalar)复合(compound)集合3 种类型:

类型写法描述值举例
i8, i16, i32, i64,<br>u8, u16, u32, u64i:带符号 <br>u:无符号 <br>数字代表存储位数42,<br>-5i8, 0x400u16, 0o100i16,<br>20_922_789_888_000u64,<br>b'*' (u8 byte literal)
isize, usize带符号/无符号 整型 <br>存储位数与系统位数相同 <br>(32 或 64 位整数)137,<br>-0b0101_0010isize,<br>0xffff_fc00usize
f32, f64IEEE 标准的浮点数,单精度/双精度1.61803, 3.14f32,<br>6.0221e23f64
bool布尔型true, false
charUnicode 字符 <br>存储空间固定为 4 字符'*', '\n', '字', '\x7f', '\u{CA0}'
(char, u8, i32)元组 tuple:可以存储多种类型('%', 0x7f, -1)
()单元类型,实际上是空 tuple()
struct S{x: f32, y: f32 }命名元素结构体,数据成员有变量名的结构体struct S { x: 120.0, y: 209.0 }
struct T(i32, char)元组结构体,数据成员无名称,形如元组,不可与元组混淆struct T(120, 'X')
struct E单元型结构体,没有数据成员E
enum Attend {<br>OnTime, Late(u32)<br>}枚举类型,枚举类型默认没有比较是否相等的运算,更没有比较大小Attend::Late(5),<br>Attend::OnTime
Box <Attend>Box 指针类型,指向堆内存中的一个泛型值Box::new(Late(15))
&i32, &mut i32只读引用和可变引用,物所有权,生命周期不能超过所指向的值。<br>只读引用也叫共享引用,&s.y, &mut v
String字符串,UTF-8 格式存储,长度可变"编程".to_string()<br>to_string 函数返回一个字符串类型
&strstr 的引用,指向 UTF-8 文本的指针,无所有权"そば: soba", &s[0..12]
[f64; 4], [u8; 256]数组,固定长度,内部数据类型必须一致[1.0, 0.0, 0.0, 1.0],<br>[b' '; 256]
Vec <f64>Vector 向量,可变长度,内部数据类型必须一致vec![0.367, 2.718, 7.389]
&[u8..u8],<br>&mut [u8..u8]切片引用,通过起始索引和长度指向数组或向量的一部分连续元素&v[10..20], &mut a[..]
&Any, &mut Readtraid 对象:实现了某 trait 内方法的对象 <br>示例中 Any、Read 都是 traitvalue as &Any,<br>&mut file as &mut Read
fn(&str, usize) -><br>isize函数类型,可以理解为函数指针i32::saturating_add
闭包闭包|a, b| aa + bb

上表中没有 byte 类型,是因为 Rust 压根就没有 byte 类型,实际上等于 u8,在一般计算中认为是 u8,

在文件或网络中读写数据时经常称为 byte 流。

标量类型(scalar)

标量类型表示一个单独的值,包括:

  • 布尔类型(bool)
  • 数值(numeir)
  • 字符 (char)

布尔类型(bool)

  • bool 类型占一个 byte, 从而允许指针指向其地址;
  • bool 型取值: true, false
  • as运算符可以将 bool 值转换为整数类型,false -> 0,true 转换为 1;
  • as不会从数值类型转换为 bool
1
2
3
4
5
6
let b1: bool = true;
let b2 = !b1;
let b3 = 1 > 0;
let i = b1 as u8;     // bool -> u8
// let b = i as bool  // u8 -> bool 非法
println!("{} {} {}", b1, b2, b3);    //true, false, true

数值类型(numeric)

  • 不允许隐式转换;
  • 如果需要调用类型的方法, 在有二意的情况下必须显式转换, 例如加后缀: (2.0_f64).sqrt()
  • bool, char, enum 可以转换为任意整型 integer, 但反向转换不行, 唯一例外是 u8char
  • 有符号整形: i8, i16, i32, i64;
  • 无符号整形: u8, u16, u32, u64;
  • 带符号/无符号整型, 存储位数与系统位数相同: isize, usize;
  • 单/双精度浮点型: f32, f64
  • 带符号整型,使用最高一位(bit)表示为符号,0 为正数,1 为负数,其他位是数值,用补码表示;
  • Rust 要求数组的索引必须是 usize 类型,在一些数据结构中,数组和向量的元素数也是 usize 型;
  • 数值类型可以通过 as运算符进行转换;
  • as运算符在转换时,对存储的数字并不改动,只是把数读出来的时候进行截取、扩展、或决定是否采用补码翻译;
  • f32 和 f64 类型都定义了一些特殊值常量: INFINITY(无穷大)、 NEG_INFINITY (负无穷)、NAN (非数字)、MIN(最小值)、MAX (最大值)。std::f32::consts 和 std::f64::consts 模块定义了一些常量:E(自然对数)、PI(圆周率)、SQRT_2(2 的平方根)等等。

各整数类型的取值范围:

  • u8:    [0, 2^8 –1] (0 ~ 255)
  • u16: [0, 2^16-1] (0 ~ 65,535)
  • u32: [0, 2^32-1] (0 ~ 4,294,967,295)
  • u64: [0, 2^64-1] (0 ~ 18,446,744,073,709,551,615,约 1.8 千亿亿)
  • i8:  [−2^7, 2^7 −1] (−128 ~ 127)
  • i16: [−2^15, 2^15 −1] (−32,768 ~ 32,767)
  • i32: [−2^31, 2^31 −1] (−2,147,483,648 ~ 2,147,483,647)
  • i64: [−2^63 , 2^63 −1] (−9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807)
  • usize:[0,2^32 −1] (32 位系统) 或 [0, 2^64 −1] (64 位系统)
  • isize:  −2^31  至 2^31 −1, or −2^63  至 2^63 −1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// int
let big_val = std::i32::MAX; //MAX 是std::i32中定义的常量,表示i32型的最大值,即231-1
let neg_inf = std::f32::NEG_INFINITY; //负无穷大;
let x = big_val + 1;         // 发生异常 panic: arithmetic operation overflowed
// 整形进制
let u07 = 0xFF_u32;          //0x 16进制
let u08 = 0o7777_u32;        //0o 8进制
let u09 = 0b01_10_00_u8;     //0b 2进制
println!("{} {} {}",u07,u08,u09);    //255 4095 24
// as
assert_eq!(  10_i8 as u16,    10_u16); // 正数值由少位数转入多位数
assert_eq!( 2525_u16 as i16, 2525_i16); // 正数值同位数转换
assert_eq!(  -1_i16 as i32,    -1_i32); // 负数少位转多位执行符号位扩展
assert_eq!(65535_u16 as i32, 65535_i32); // 正数少位转多位执行0位扩展(也可以理解为符号位扩展)
//由多位数转少位数,会截掉多位数的高位,相当于多位数除以2^N的取模,其中N是少位数的位数
assert_eq!( 1000_i16 as u8, 232_u8); //1000的二进制是0000 0011 1110 1000,截掉左侧8位,留下右侧8位,是232
assert_eq!(65535_u32 as i16, -1_i16); //65535的二进制,16个0和16个1,截掉高位的16个0,剩下的全是1,全1的有符号补码是-1

//同位数的带符号和无符号相互转化,存储的数字并不动,只是解释的方法不一样
//无符号数,就是这个值;而有符号数,需要用补码来翻译
assert_eq!(-1_i8  as u8, 255_u8);  //有符号转无符号
assert_eq!(255_u8 as i8, -1_i8);  //无符号转有符号
// float
assert_eq!(5f32.sqrt() * 5f32.sqrt(), 5.); // 平方根,此外还有sin()、ln()等诸多数学计算方法
assert_eq!(-3.7f64.floor(),  -4.0);  //向下取整,还有ceil()方法是向上取整,round()方法是四舍五入)
assert_eq!(1.2f32.max(2.2), 2,2); //比较返回最大值,min()方法是取最小值
assert_eq!(-3.7f64.trunc(), -3.0);  //删除小数部分,注意和floor、ceil的区别
assert!((-1. / std::f32::INFINITY).is_sign_negative()); //是否为负值,注意-0.0也算负值

位运算

名字运算符说明范例
位与&相同位都是 1 则返回 1 否则返回 0A & B ==2
位或|相同位只要有一个是 1 则返回 1 否则返回 0A|B = 3
异或^相同位不相同则返回 1 否则返回 0A ^ B = 1
位非!把位中的 1 换成 0 , 0 换成 1(!B) 结果 -4
左移<<操作数中的所有位向左移动指定位数,右边的位补 0(A << 1) 结果为 4
右移>>操作数中的所有位向右移动指定位数,左边的位补 0(A >> 1) 结果为 1

byte 字面量

  • Rust 没有 byte 类型,u8 类型相当于 byte 类型;
  • byte 字面量,表示 ASCII 字符;
  • 书写方式: b'x',b 表示是 byte;
ASCII 字符byte 字面量的书写相当于的数值
单引号   'b'''39_u8
反斜杠 \b'\'92_u8
换行b'\n'10_u8
回车b'\r'13_u8
制表符 Tabb'\t'9_u8

字符(char)

  • Rust 的 char 表示一个 Unicode 字符;
  • 每个 char 固定为 4 个 byte,32bit;
  • UNICODE 编码;
  • char 不能和任何其他类型之间进行隐式转换;
  • 使用 as运算符将 char 转换为整数类型;
  • 对于小于 32 位的类型,字符值的高位将被截断:
  • 只有 u8 能用 as 转换为 char。
  • 如果想用 u32 位转换为 char,可以用 std 库里的 std::char::from_u32()函数,返回值是 Option<char>类型
  • char 类型的书写是用单引号引起来,字符串是用双引号引起来:
  • char 类型的值包含范围为 0x0000 到 0xD7FF 或 0xE000 到 0x10FFFF 的 Unicode 码位。
  • 对于其他数值,Rust 会认为是无效的 char 类型,出现编译异常。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
assert_eq!('*' as i32, 42);    // char -> i32
assert_eq!('ಠ' as u16, 0xca0);
assert_eq!('ಠ' as i8, -0x60); // U+0CA0 被截断为8位带符号整型

//std::char模块中有很多有用的char方法/函数:
assert_eq!('*'.is_alphabetic(), false);  //检查是否是字母
assert_eq!('β'.is_alphabetic(), true);
assert_eq!('8'.to_digit(10),  Some(8));  //检查是否数字
assert_eq!('ಠ'.len_utf8(), 3);  //用utf-8格式表示的话,占据几个字节
assert_eq!(std::char::from_digit(2,  10), Some('2'));  //数字转换为char,第二个参数是进制

// char -> Integer
println!("{}", '我' as i32);     // 25105
println!("{}", '是' as u16);     // 26159
println!("{}", '是' as u8);      // 47,被截断了

// u8 -> char
println!("{}", 97_u8 as char);    // 'a'

// std::char
use std::char;

// digit -> char
println!("{}", char::from_u32(0x2764).unwrap());  // ❤
assert_eq!(char::from_u32(0x110000), None);  // true

println!("{}", char::from_digit(4,10).unwrap());  // '4'
println!("{}", char::from_digit(11,16).unwrap()); // 'b'
assert_eq!(char::from_digit(11,10),None); // true

char 类型与 byte 字面量,String 类型区别:

  • 'C': char 类型,在 stack 上开辟 4 字节空间,把字母 C 的 Unicode 码 0x 00 00 00 43存入;
  • b'C':byte 型,在 stack 上开辟 1 字节空间,把字母 C 的 ASCII 码 0x43存入;
  • "C":String, 在 heap 内存上开辟 N 字节空间(N 一般是字母 C 的字节数 1),然后在 stack 内存上开辟 12 字节空间(此处以 32 位平台为例):
    • 4 个字节存放堆内存放置数据的指针,
    • 4 个字节存放字符串在内存中开辟的空间 N;
    • 4 个字节存放字符串当前使用的空间;

复合类型

复合(compound)类型由多个值组合而成。复合类型包括:

  • Tuple: 多个类型的值组合进一个类型;
  • Struct:
  • Enum: 多种数值中的一种;
  • Union:

元组(Tuple)

元组是一个将多个其他类型的值组合进一个复合类型的主要方式。

  • 包含在 ()中的 ,分隔的值列表 (T1, T2, ...)
  • 元组长度固定,长度不会增大或缩小;
  • 元组分配在栈空间;
1
2
3
4
5
6
7
8
9
// 元组定义
let tup1: (i32, f64, u8) = (500, 6.4, 1);
let tup2 = (500, 6.4, 1);
// 元组解构
let (x, y, z) = tup1;
// 索引访问
let five_hundred = tup2.0;
let six_point_four = tup2.1;
let one = tup3.2;

结构体(Struct)

Rust 中的结构体

Rust 提供了 3 种结构体:

  • 具名结构体:结构体的每个字段拥有名称;
  • 元组结构体: 结构体的字段没有名称;
  • 单元结构体: 结构体不包含任何字段;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
struct Point {x: f32,  y: f32, }  // 具名结构体
struct Color(i32, i32, i32);  // 元组结构体
struct Integer(u32);          // 元组结构体只有一个字段时,称为NewType模式
struct Nil;                   // 单元结构体,没有任何字段的结构体。单元结构体实例就是其本身。

// 结构体实例化
let point: Point = Point { x: 0.3, y: 0.4 };
println!("point coordinates: ({},{})", point.x, point.y); // 访问 point 的字段
// let解绑struct
let Point {x: my_x, y: my_y} = point;

// struct tuple
struct Pair(i32, f32);  //struct tuple
let pair = Pair(1, 0.1);
println!("pair contains {:?} and {:?}", pair.0, pair.1);
let Pair(integer, decimal) = pair;
println!("pair contains {:?} and {:?}", integer, decimal);

枚举(Enum)

  • enum用于表示多种可能数值中的一种;
  • enum的多个值共用一个存储空间;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 不带参数枚举
enum Number {
    Zero,
    One,
}

// 带参数枚举
enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

enum MyEnum {
    Foo,
    Bar,
}

// enum 格式化输出
impl std::fmt::Display for MyEnum {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            MyEnum::Foo => write!(f, "Foo"),
            MyEnum::Bar => write!(f, "Bar"),
        }
    }
}

联合体(Union)

  • union和 enums 类似,但没有标签;
  • union的所有字段共享同一段存储;
  • union的尺寸由其尺寸最大的字段的尺寸所决定;
  • union字段的读取必须放在非安全(unsafe)块里;
  • union没有“活跃字段(active field)”。每次访问联合体只是用所指定的字段的类型解释此联合体的存储;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#[repr(C)]
union MyUnion {
    f1: u32,
    f2: f32,
}

let u = MyUnion { f1: 1 }; //union定义
unsafe { u.f1 = 5 };    //union写入是不安全的
let value = unsafe { u.f1 }; //uion读取是不安全的
// union模式匹配
unsafe {
    match u {
        MyUnion { f1: 10 } => { println!("ten"); }
        MyUnion { f2 } => { println!("{}", f2); }
    }
}

集合类型

集合类型由多个相同类型的值组成,包括:

  • 数组(array):
  • 向量(vector)

数组(Array)

在一块连续空间内存中,存储了一系列的同样类型的数据

  • 数组的类型为 [T; N]T表示元素类型,N为元素个数;
  • 只有当 T, N 都相同时,数组的类型才相同;
  • Rust 数组大小固定,一旦声明,其长度不会增大或缩小;
  • 数组位于 上;
  • array 索引访问错误会产生 panic;
  • array只能储存一种类型的数据,无法存储不同类型的数据;
1
2
3
4
5
6
7
// 数组定义
let a: [i32; 5] = [1, 2, 3, 4, 5];
let array: [i32; 4] = [42, 10, 5, 2];
// let a = [1,"string"];   //错误,数组元素类型应该相同
let color = ["Red", "Yellow", "Blue"];
let second = a[1];   // 数组元素索引访问
let l = a.len()      // 数组长度

向量(Vector)

  • Vec是动态数组,会自动增长,不会自动收缩;

  • Vector 包含两部分:

    • 栈上的 vector 结构体:

      • 指向堆上连续空间的首指针;
      • 总元素个数;
      • 已使用元素个数;
    • 堆上的连续分配空间;

  • Vec<T>中的泛型 T 必须是 Sized的;

  • vector 把所有的元素放在一个分配在堆(heap)上的 array 上。当一个新元素被 push 进来时,vector 检查 array 是否有足够的剩余空间。如果空间不足,vector 就分配一个更大的 array,将所有的元素都拷贝到这个新的 array 中,然后释放旧的 array

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// vector初始化
let v1: Vec<i32> = Vec::new();        //Vec::new()
let v2 = vec![1,2,3];                 //[1,2,3]
let v21 = vec![false; 4];                 //[3,3, 3, 3]
let v3: Vec<_> = (1..5).collect();    //[1,2,3,4]
let v4 = Vec::with_capacity(5);       //预留空间初始化

// vector元素访问
let third: &i32 = &v[2];                //[]运算符
let third: Option<&i32> = v.get(2);     //get()方法

// 遍历
for i in &v { .. } // 获得引用
for i in &mut v { .. } // 获得可变引用
for i in v { .. } // 获得所有权,注意此时Vec的属主将会被转移!!
// 迭代器
for x in xs.iter() {
        println!("> {}", x);
}
// 内置方法
let mut v = vec![11,22];
v.len();                         //v长度(元素数量)
v.is_empty();                    //vec是否为空
v.get(1);                        //get
v.iter();                        //iterator
v.push(33);                      //在尾部插入元素33
v.resize(10, 0);                 //
v.insert(0, 111);                //在索引0处插入111
v.remove(1);                     //删除i元素并返回被删除的元素,索引越界将panic报错退出
v.pop();                         //删除并返回vec尾部的元素,vec为空则返回None
v.append(vec![3, 4]);            //将另一个vec中的所有元素追加移入vec中,移动后另一个vec变为空vec
v.truncate(2);                    //将vec截断2,多余的元素被删除
v.retain(|x| *x > 20);            //保留满足条件的
v.drain(1..=3);                   //删除指定范围的元素,同时返回该范围所有元素的迭代器
v.split_off(2);                   //从index 2处分裂为两个vec
v.clear();                        //清空vec

切片(Slice)

  • 切片(slice)是对数组(array)或 vector 的一个临时视图;

  • slice 是动态大小类型,无法直接被使用,只能通过引用形式来访问;

  • slice 形式:[T]

  • Slice 继承了 Array 的 length、item 等很多的方法;

  • slice 包含 2 个字段:

    • 指向 slice 起始元素的头指针;
    • slice 中元素的个数;
1
2
3
let v = [1, 2, 3, 4];  //定义一个数组;
let s1 = &v[..];       //获取数组的切片引用;
let s = &v[1..3];      //获取数组切片[2,3];

范围(Range)

  • Range 是 Rust 内置的用于表示一个范围的数据类型;
  • Range 数据类型位于 std::ops::RangeXXX内;
  • 每个范围都是一个迭代器,可用 for 循环打印范围内的元素;

Rust 支持范围操作符,有以下几种表示范围的操作符:

范围表达式类型表示的范围
start..endstd::ops::Rangestart ≤ x < end
start..std::ops::RangeFromstart ≤ x
..endstd::ops::RangeTox < end
..std::ops::RangeFull-
start..=endstd::ops::RangeInclusivestart ≤ x ≤ end
..=endstd::ops::RangeToInclusivex ≤ end
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// range结构体定义
pub struct Range<Idx> {
    pub start: Idx,
    pub end: Idx,
}

// range的使用
use std::ops::{Range, RangeInclusive};

assert_eq!((1..5), Range{ start: 1, end: 5 });
// (1..=5)是结构体std::ops::RangeInclusive的一个实例
assert_eq!((1..=5), RangeInclusive::new(1, 5));
// 自带的 sum 方法用于求和
assert_eq!(3+4+5, (3..6).sum());
assert_eq!(3+4+5+6, (3..=6).sum());
(3..6)

// 每个范围都是一个迭代器,可用for 循环打印范围内的元素
for i in (1..5) {
    println!("{}", i);
}
for i in (1..=5) {
    println!("{}", i);
}
// 逆序
for i in (1..=5).rev() {
    println!("{}", i);
}

// 范围操作
let arr = [11, 22, 33, 44, 55];
let s1 = &arr[0..3];    // [11,22,33]
let s2 = &arr[1..=3];   // [22, 33, 44]
let s3 = &arr[..];      // [11, 22, 33, 44, 55]

常用数据类型

字符串

Rust 中的字符串包括两种:strString

str(字符串字面量)

  • str由核心语言提供 core::str

  • 常以引用的形式出现——&str

  • 静态不可变;

  • 支持转义;

  • 支持切片操作;

  • &str有两种产生方式:

    • 字符串字面量:&str内存位于程序的预分配文本区,该区域为只读区域;
    • 字符串引用:为一个 String的切片引用,原 String字符位于堆上;
1
2
3
4
5
6
7
8
//定义
let x = "Hello";                // 字符串字面量
let x: &'static str = "Hello";  //完整形式
//转义
let z = "foo
         bar";
let w = "foo\n      bar";
assert_eq!(z, w);

String(字符串)

  • String 由标准库提供的 std::String

  • 位于堆(heap)上分配内存;

  • 可增长的、可变的、有所有权;

  • String&str 都是 UTF-8 编码的;

  • String -> &str非常轻松,几乎没有开销;

  • &str -> String是需要在堆上请求内存的;

  • String不支持通过下标访问,可转换为 &str进行;

  • 不是 char 数组, 而是 UTF-8变长序列;

  • ASCII 字符占一个字节 byte, 其余字符占多个不定长字节.

  • len()方法返回的是字节(byte)长度, 而非字符(char)长度

  • chars().count()返回字符(char)长度

  • 一个栈上的 String 变量在内存中表示如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
//新建String
let s1 = String::new();                 //new: 创建空字符串
//&str -> String
let s2 = "string".to_string()            //to_string: 创建非空字符串
let s3 = String::from("string2")         //from: 创建非空字符串
let mut s4 = String::from("foo");
let s5 = String::from("bar");
//raw string
let raw_str = r"Escapes don't work here: \x3F \u{211D}";
assert_eq!(raw_str, "Escapes don't work here: ? ℝ");
let quotes = r#"And then I said: "There is no escape!""#;
println!("{}", quotes);
// 如果希望在字符串中使用 # 号,可以如下使用:
let  delimiter = r###"A string with "# in it. And even "##!"###;
println!("{}", delimiter);
// String -> &str
let str1: &str = s2.as_str();        //
let str2 = &*s2; //String 实现impl Deref<Target=str> for String
let str2 = &s2;  //string -> &str
//更新字符串
s4.push_str("bar");                   //push_str: 添加字符串
s4.push_str(&s2)                      // 这里是&s2 而不是s2
s4.push('l')                          //push: 添加字符
//连接字符串
// let s3 = s1 + "wold";
let s3 = s1 + &s2;   //s1 被移动了,不能继续使用
//格式化字符串
let s = format!("{}-{}-{}", s1, s2, s3);  //format!宏不会获取参数的所有权

// Vec<char> -> String
let s4 = String::from_iter(vec!['a', 'b', 'c'].iter()); //s4 = "abc"

// 字符串slice
let mut s1 = String::from("hello");   //&str -> String
let s2 = "wold";
println!("{}", &s1[..1], &s2[1..2]); // 不要忘了 & 符号

// string -> &[u8]
let u8_bytes = s1.as_bytes();   // &[104, 101, 108, 108, 111],
let chars = s2.chars();     //Chars

s1.chars().nth(2)  // 第2个字符
// 遍历字符
for c in "abcd".chars() {
    println!("{}", c); // a,b,c,d
}
// 遍历byte
for b in "नमस्ते".bytes() {
    println!("{}", b); // 224 164 168 224 ...
}
// 插入
s1.insert(5, ',');   //插入字符
s1.insert_str(6, " I like");  //插入字符串字面量
// 替换
s1.replace("rust", "RUST");       //替换所有子串,返回替换后的新字符串
s1.replacen("rust", "RUST", 1);   //替换1个子串,返回替换后的新字符串
s1.replace_range(7..8, "R");
// 删除
s1.pop();         //删除s1尾部字符;
s1.remove(0);     //删除s1第0个字节的字符, 如果参数不在字符的字节边界,则发生错误;
// 截断
s1.truncate(3);   //截断s1从第3个字节开始到最后的所有字符;
s1.clear();       //清空字符串所有字符;
// 连接
let s2 = s1 + "abc";  //连接字符串字面量;
s1 += "cdf";   //s1为mut

//字符串切片
let s = String::from("Hello word");
let hello = &s[0..5];       // hello: &str
let word = &s[6..11];       // word: &str

//
use utf8_slice;
let s = "The 🚀 goes to the 🌑!";
let rocket = utf8_slice::slice(s, 4, 5);  // 结果是 "🚀"

哈希表(HashMap)

  • 所有实现了 EqHash的类型都可以作为 HashMap 的 key;
  • 通过 #[derive(PartialEq, Eq,hash)]可以快速的实现 EqHash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//引入hashmap包
use std::collections::HashMap

//new
let mut scores = HashMap::new();        //new
let solar_distance = HashMap::from([    //from
    ("Mercury", 0.4),
    ("Venus", 0.7),
    ("Earth", 1.0),
    ("Mars", 1.5),
]);

scores.insert(String::form("Blue"), 10);    //插入
scores.insert(String::from("Yellow"), 50);

let teams = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];
// zip, 合并k-v
let scores: HashMap<_,_> = teams.iter().zip(initial_scores.iter()).collect();

let score = scores.get(String::form("Blue")); //get

//遍历
for (key, value) in &scores {
  println!("{}: {}", key, value);
}

// entry
// or_insert 方法会返回这个键的值的一个可变引用(&mut V)
scores.entry(String::from("Yellow")).or_insert(50);         //如果存在就忽略,如果不存在就插入
*scores.entry(String::from("Yellow")).or_insert(10) += 10;  //更新
scores2.entry(String::from("Yellow")).or_default().push(50);  //string->vec<i32> 插入
  • Rust 中的 HashSet 实现非常简单就是 HashMap<T,()>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::collections::HashSet;
// Type inference lets us omit an explicit type signature (which
// would be `HashSet<String>` in this example).
let mut books = HashSet::new();

// Add some books.
books.insert("A Dance With Dragons".to_string());
books.insert("To Kill a Mockingbird".to_string());
books.insert("The Odyssey".to_string());
books.insert("The Great Gatsby".to_string());

// Check for a specific one.
if !books.contains("The Winds of Winter") {
    println!("We have {} books, but The Winds of Winter ain't one.",
             books.len());
}

// Remove a book.
books.remove("The Odyssey");

// Iterate over everything.
for book in &books {
    println!("{book}");
}

有序映射(BTreeMap)

  • key有序的k-v存储结构;
  • 底层使用BTree;
  • 通过迭代器可按 key 顺序访问 BtreeMap 中的所有元素;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
use std::collections::BTreeMap;

// new方法
let mut movie_reviews = BTreeMap::new();
// insert
movie_reviews.insert("Office Space",       "Deals with real issues in the workplace.");
movie_reviews.insert("Pulp Fiction",       "Masterpiece.");
movie_reviews.insert("The Godfather",      "Very enjoyable.");
movie_reviews.insert("The Blues Brothers", "Eye lyked it a lot.");

// from 
let solar_distance = BTreeMap::from([
    ("Mercury", 0.4),
    ("Venus", 0.7),
    ("Earth", 1.0),
    ("Mars", 1.5),
]);

有序集合(BTreeSet)

  • BTreeSet 为有序集合, 和 hashset 区别在于集合中的元素为有序集;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use std::collections::BTreeSet;

// 类型推断允许我们省略显式类型签名(它
// 在这个例子中将是 `BTreeSet<&str>`)。
let mut books = BTreeSet::new();

// 添加一些书籍。
books.insert("A Dance With Dragons");
books.insert("To Kill a Mockingbird");
books.insert("The Odyssey");
books.insert("The Great Gatsby");

// 检查一个特定的。
if !books.contains("The Winds of Winter") {
    println!("We have {} books, but The Winds of Winter ain't one.",
             books.len());
}
books.remove("The Odyssey");  // 删除一本书。

// 遍历所有内容。
for book in &books {
    println!("{book}");
}

双端队列(VecDeque)

  • 基于可扩张的环形缓冲双端队列;

二叉堆/优先队列(BinaryHeap)

  • BinaryHeap 是 rust 中的;
  • BinaryHeap 默认为大端堆, 即最大值在堆顶, 通过使用 Reverse得到小端堆;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::collections::BinaryHeap;

let mut heap = BinaryHeap::new();
let heap2 = BinaryHeap::from([1, 5, 2]);  //

assert_eq!(heap.peek(), None);
heap.push(93);
heap.push(80);
heap.push(48);
assert_eq!(heap.peek(), Some(&93));

use std::cmp::Reverse;

let mut heap = BinaryHeap::new();

// 在 `Reverse` 中包装值
heap.push(Reverse(1));
heap.push(Reverse(5));
heap.push(Reverse(2));

// 如果我们现在弹出这些分数,它们应该以相反的顺序返回。
assert_eq!(heap.pop(), Some(Reverse(1)));
assert_eq!(heap.pop(), Some(Reverse(2)));
assert_eq!(heap.pop(), Some(Reverse(5)));
assert_eq!(heap.pop(), None);

高级类型

类型别名

  • Rust使用 type 关键字来给予现有类型另一个名字;

  • type定义的类型别名拥有和原类型同一类型;

  • 类型别名的主要用途是减少重复;

1
type Kilometers = i32;

never type

dyn

sized

参考

  1. https://www.codemore.top/p/ce5ae822-e775-351d-830c-994110faf023/
  2. https://unpluggedcoder.me/2019/08/15/Rust%E5%85%A5%E9%97%A8%E5%A4%B1%E8%B4%A5%E4%B9%8BOwnership/
  3. Rust 之路(2)——数据类型 上篇 - sumyuan - 博客园
  4. https://doc.rust-lang.org/std/collections/struct.VecDeque.html
  5. https://rustwiki.org/zh-CN/edition-guide/rust-2018/data-types/union-for-an-unsafe-form-of-enum.html
  6. https://rustwiki.org/zh-CN/std/collections/binary_heap/struct.BinaryHeap.html
updatedupdated2024-05-102024-05-10