Rust基础[part4]_基本类型,所有权

发布于:2025-07-17 ⋅ 阅读:(13) ⋅ 点赞:(0)

Rust基础[part4]_基本类型,所有权

Rust类型

概览

以下是整合后的 Rust 类型说明表格:

类型 说明
i8i16i32i64i128u8u16u32u64u128 给定位宽的有符号整数和无符号整数 42-5i80x400u160o100i1620_922_789_888_000u64b'*'u8 字节字面量)
isizeusize 与机器字(32 位或 64 位)一样大的有符号整数和无符号整数 137-0b0101_0010isize0xffff_fc00usize
f32f64 单精度 IEEE 浮点数和双精度 IEEE 浮点数 1.618033.14f326.0221e23f64
bool 布尔值 truefalse
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 对整数浮点数的类型后缀语法有细微差别:

  • 整数:类型后缀直接紧跟数值,无下划线
    例如:1u3242i640xFFu8
  • 浮点数:类型后缀与数值之间必须有下划线_)。
    例如:0.5_f323.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 通过所有权系统在编译期解决内存安全问题,无需垃圾回收,同时保持高性能。

基本规则

  1. 每一个值都有所有者(owner);
  2. 在任一时刻,值都只有一个所有者;
  3. 当所有者离开作用域(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的引用
}

后面会详细讲到引用的用法


网站公告

今日签到

点亮在社区的每一天
去签到