Rust基础[part4]_基本类型,所有权
Rust基础[part4]_基本类型,所有权
Rust类型
概览
以下是整合后的 Rust 类型说明表格:
类型 | 说明 | 值 |
---|---|---|
i8 、i16 、i32 、i64 、i128 、u8 、u16 、u32 、u64 、u128 | 给定位宽的有符号整数和无符号整数 | 42 、-5i8 、0x400u16 、0o100i16 、20_922_789_888_000u64 、b'*' (u8 字节字面量) |
isize 、usize | 与机器字(32 位或 64 位)一样大的有符号整数和无符号整数 | 137 、-0b0101_0010isize 、0xffff_fc00usize |
f32 、f64 | 单精度 IEEE 浮点数和双精度 IEEE 浮点数 | 1.61803 、3.14f32 、6.0221e23f64 |
bool | 布尔值 | true 、false |
char | Unicode 字符,32 位宽(4 字节) | '*' 、'\n' 、'字' 、'\x7f' 、'\u{...}' (Unicode 转义) |
(char, u8, i32) | 元组,允许混合类型 | ('%', 0x7f, -1) |
() | “单元”(空元组) | () |
struct S { x: f32, y: f32 } | 具名字段型结构体 | S { x: 120.0, y: 209.0 } |
struct T(i32, char); | 元组型结构体 | T (120, 'X') |
struct E; | 单元型结构体,无字段 | E |
enum Attend { OnTime, Late(u32) } | 枚举,或代数数据类型 | Attend::Late(5) 、Attend::OnTime |
Box<Attend> | Box :指向堆中值的拥有型指针 | Box::new(Late(15)) |
&i32 、&mut i32 | 共享引用和可变引用:非拥有型指针,其生命周期不能超出引用目标 | &s.y 、&mut v |
String | UTF-8 字符串,动态分配大小 | " ラーメン : ramen".to_string() |
&str | 对 str 的引用:指向 UTF-8 文本的非拥有型指针 | " そば : soba" 、&s[0..12] |
[f64; 4] 、[u8; 256] | 数组,固定长度,其元素类型都相同 | [1.0, 0.0, 0.0, 1.0] 、[b' '; 256] |
Vec<f64> | 向量,可变长度,其元素类型都相同 | vec![0.367, 2.718, 7.389] |
&[u8] 、*mut [u8] | 对切片(数组或向量某一部分)的引用,包含指针和长度 | &v[10..20] 、&mut a[..] |
Option<&str> | 可选值:或者为 None (无值),或者为 Some(v) (有值,其值为 v ) | Some("Dr.") 、None |
Result | 可能失败的操作结果:或者为成功值 Ok(v) ,或者为错误值 Err(e) | Ok(4096) 、Err(Error::last_os_error()) |
&dyn Any 、&mut dyn Read | 特型对象,是对任何实现了一组给定方法的值的引用 | value as &dyn Any 、&mut file as &mut dyn Read |
fn(&str) -> bool | 函数指针 | str::is_empty |
(闭包类型没有显式书写形式) | 闭包 | ` |
x..=y | 序列 | 1..=4 |
整型
大小 | 有符号 | 无符号 |
---|---|---|
8 位 | i8 | u8 |
16 位 | i16 | u16 |
32 位 | i32 | u32 |
64 位 | i64 | u64 |
128 位 | i128 | u128 |
视架构而定 | isize | usize |
pub fn print_int_show() {let integer: i32 = 2147483647; // 最大值let integer: i32 = -2147483648; // 最小值let integer: i32 = 0x1F; // 十六进制let integer: i32 = 0o17; // 八进制let integer: i32 = 0b1111_0000; // 二进制let integer: i32 = 1_000_000; // 使用下划线分隔数字let integer: u8 = b'A'; // 字符转换为整数
}
浮点型
浮点类型数数字,带有小数点,基本两种类型是:f32和f64
默认为f64,精度更高,速度基本和f32相同
但是一般在区块链应用中,一般都使用i128, 因为精度的问题
精度问题
精度问题,Rust 作为一门注重内存安全和性能的系统级编程语言,在处理数值精度时遵循明确的规则,但也存在一些需要开发者注意的潜在陷阱。
assert_eq!(0.1 + 0.2, 0.3); // 浮点数相加 这里会failed
println!("0.1 + 0.2 = {:x}", (abc.0 + abc.1).to_bits());println!("0.3 = {:x}", abc.2.to_bits());println!("0.1 + 0.2 = {:x}", (xyz.0 + xyz.1).to_bits());println!("0.3 = {:x}", xyz.2.to_bits());
结果,发现高精度的0.1 + 0.2 的结果和0.3的结果是不同的。
0.1 + 0.2 = 3e99999a
0.3 = 3e99999a
0.1 + 0.2 = 3fd3333333333334
0.3 = 3fd3333333333333
这就是精度问题。
Rust 对整数和浮点数的类型后缀语法有细微差别:
- 整数:类型后缀直接紧跟数值,无下划线。
例如:1u32
、42i64
、0xFFu8
。- 浮点数:类型后缀与数值之间必须有下划线(
_
)。
例如:0.5_f32
、3.14159_f64
。
NAN
NAN(Not a Number) , 用来定义如以下这个平方根,这种数学上未定义的结果。
pub fn nan_example() {let nan_value: f64 = (-1.1_f64).sqrt(); // 计算负数的平方根assert!(nan_value.is_nan()); // 检查是否为NaNprintln!("NaN: {}", nan_value);
}
布尔类型
占一个字节
pub fn boolean_example() {let t = true;let f: bool = false;println!("true: {}, false: {}", t, f);
}
字符
单引号去声明的,unicode字符也可以作为Rust的字符 ,占用四个字节
pub fn char_example() {let c: char = 'A';let emoji: char = '😊';// unicode字符也可以作为Rust的字符println!("char: {}, emoji: {}", c, emoji);
}
序列
包含需要加上=
pub fn _sequence_example() {let seq = 1..=5; // 包含5的范围for i in seq {println!("Sequence: {}", i);}
}
类型强转
as关键字用于在原始类型之间进行类型转换,不适用于复合类型,比如String或其他自定义类型
pub fn convert() {let a: i32 = 10;let b: f64 = a as f64; // 将i32转换为f64println!("Converted value: {}", b);let c: f64 = 3.14;let d: i32 = c as i32; // 将f64转换为i32println!("Converted value: {}", d);
}
练习
https://practice-zh.course.rs/basic-types/numbers.html
错题记录:
// 填空
use std::ops::{Range, RangeInclusive};
fn main() {assert_eq!((1..5), Range{ start: 1, end: 5 });assert_eq!((1..=5), RangeInclusive::new(1, 5));
}
// 填空,并解决错误
fn main() {// 整数加法assert!(1u32 + 2 == 3);// 整数减法assert!(1i32 - 2 == -1);assert!(1i8 - 2 == -1);assert!(3 * 50 == 150);assert!(9.6 / 3.2 - 3.0 < 1e-9); assert!(24 % 5 == 4);// 逻辑与或非操作assert!(true && false == false);assert!(true || false == true);assert!(!true == false);// 位操作println!("0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101);println!("0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101);println!("0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101);println!("1 << 5 is {}", 1u32 << 5);println!("0x80 >> 2 is 0x{:x}", 0x80u32 >> 2);
}
所有权
零开销内存回收的一种高效实现方式
Rust是一种系统编程语言,其设计目的是确保内存安全并防止数据竞争,而不依赖垃圾回收器。这种内存安全性主要通过所有权系统来进行实现。
为什么需要所有权?
在系统编程语言中,内存管理一直是核心挑战:
- 手动管理(如 C/C++):开发者需手动分配和释放内存,易出现悬空指针、内存泄漏等问题。
- 垃圾回收(如 Java/Go):自动回收不再使用的内存,但带来运行时开销和不可预测的停顿。
Rust 通过所有权系统在编译期解决内存安全问题,无需垃圾回收,同时保持高性能。
基本规则
- 每一个值都有所有者(owner);
- 在任一时刻,值都只有一个所有者;
- 当所有者离开作用域(scope),值会被丢弃(drop);
move语义
pub fn _ownership_example() {let s1: String = String::from("Hello");let s2: String = s1; // 转移所有权// println!("{}", s1); // 此处会报错,因为s1的所有权已转移drop(s2); // rust中可以手动释放资源,但通常不需要这样做,因为Rust会在变量超出作用域时自动释放资源; 注意s1这个时候已经是类似空指针的状态,也不需要去释放println!("{}", s2);
}
copy语义
基本数据类型
fn main() {let x = 5;let y = x; // x 的值被复制到 yprintln!("x = {}, y = {}", x, y); // 两个变量都有效
}
练习
// 用两种方法打印成功s1 和s2的值let s1 = String::from("Hello, Rust!");let s2 = ownership::take_ownership(s1); //以下代码不能修改println!("{}", s1);println!("{}", s2);
第一种方法
fn main() {// 第一种方法let s1 = String::from("Hello, Rust!");let s2 = take_ownership(s1.clone()); // 使用clone方法复制s1//以下代码不能修改println!("{}", s1);println!("{}", s2);
}
pub fn take_ownership(s: String) -> String {s // 返回的是 s的克隆
}
第二种方法
fn main() {// 第二种方法let s1 = String::from("Hello, Rust!");let s2 = take_ownership(&s1); // 传递s1的引用//以下代码不能修改println!("{}", s1);println!("{}", s2);
}// 传入类型改为引用类型
pub fn take_ownership(s: &String) -> &String {println!("Taking ownership of: {}", s);&s // 返回s的引用
}
后面会详细讲到引用的用法