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()); // 检查是否为NaN
println!("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转换为f64
println!("Converted value: {}", b);
let c: f64 = 3.14;
let d: i32 = c as i32; // 将f64转换为i32
println!("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 的值被复制到 y
println!("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的引用
}
后面会详细讲到引用的用法