目录

第三章 基本数据类型

3.1 标量类型

标量(Scalar)类型代表单个值。Rust有四种主要的标量类型:整数、浮点数、布尔值和字符。

整数类型

整数是没有小数部分的数字。

有符号整数(可正可负):

类型 范围 大小
——————
i8 -128 到 127 1字节
i16 -32,768 到 32,767 2字节
i32 -2,147,483,648 到 2,147,483,647 4字节
i64 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 8字节
i128 非常大的范围 16字节
isize 取决于架构(32位或64位) 4或8字节

无符号整数(仅非负):

类型 范围 大小
——————
u8 0 到 255 1字节
u16 0 到 65,535 2字节
u32 0 到 4,294,967,295 4字节
u64 0 到 18,446,744,073,709,551,615 8字节
u128 非常大的范围 16字节
usize 取决于架构 4或8字节

整数溢出:

在debug模式下,整数溢出会导致panic;在release模式下,会进行二进制补码环绕。

let mut x: u8 = 255;
// x = x + 1;  // debug模式panic,release模式变为0
 
// 显式处理溢出
let (result, overflowed) = x.overflowing_add(1);
println!("结果:{},溢出:{}", result, overflowed);

整数方法:

let n = 42_i32;
 
// 检查奇偶
println!("是偶数:{}", n.is_even());
 
// 转换类型
let n_u8 = n as u8;
 
// 计数位数
println!("二进制中1的个数:{}", n.count_ones());
 
// 绝对值
let m = -42;
println!("绝对值:{}", m.abs());
 
// 幂运算
println!("2的10次方:{}", 2_i32.pow(10));

浮点类型

Rust有两种浮点类型:f32(单精度)和f64(双精度)。

let x = 2.0;        // f64(默认)
let y: f32 = 3.0;   // f32

浮点运算:

// 基本运算
let sum = 5.0 + 10.0;
let difference = 95.5 - 4.3;
let product = 4.0 * 30.0;
let quotient = 56.7 / 32.2;
let remainder = 43.0 % 5.0;
 
// 数学函数
let sqrt = 4.0_f64.sqrt();
let power = 2.0_f64.powf(3.0);  // 2的3次方
let log = 100.0_f64.log10();     // 以10为底的对数
let floor = 3.7_f64.floor();
let ceil = 3.2_f64.ceil();
let round = 3.5_f64.round();

特殊浮点值:

let inf = f64::INFINITY;
let neg_inf = f64::NEG_INFINITY;
let nan = f64::NAN;
 
// 检查
println!("是正无穷:{}", inf.is_infinite());
println!("是NaN:{}", nan.is_nan());

布尔类型

let t = true;
let f: bool = false;
 
// 逻辑运算
let and = t && f;   // 逻辑与
let or = t || f;    // 逻辑或
let not = !t;       // 逻辑非

布尔类型占用1字节。

字符类型

Rust的char类型是Unicode标量值,占用4字节。

let c = 'z';
let z: char = 'ℤ';
let heart_eyed_cat = '😻';
let chinese = '中';

字符方法:

let c = 'A';
 
println!("是大写字母:{}", c.is_uppercase());
println!("是小写字母:{}", c.is_lowercase());
println!("是字母:{}", c.is_alphabetic());
println!("是数字:{}", c.is_numeric());
println!("是空白字符:{}", c.is_whitespace());
 
// 大小写转换
println!("小写:{}", c.to_lowercase());
println!("大写:{}", 'a'.to_uppercase());
 
// 转换为数字
let num_char = '9';
if let Some(digit) = num_char.to_digit(10) {
    println!("数字值:{}", digit);
}

3.2 复合类型

复合类型可以将多个值组合成一个类型。Rust有两种基本的复合类型:元组和数组。

元组(Tuple)

元组是将多个不同类型的值组合在一起的复合类型。元组有固定的长度。

// 创建元组
let tup: (i32, f64, u8) = (500, 6.4, 1);
 
// 类型推断
let tup = (500, 6.4, 1);
 
// 解构
let (x, y, z) = tup;
println!("y的值为:{}", y);
 
// 通过索引访问
let five_hundred = tup.0;
let six_point_four = tup.1;
let one = tup.2;

单元类型(Unit Type):

let unit = ();  // 空元组,表示"无返回值"

嵌套元组:

let nested = ((1, 2), (3, 4, 5));
let ((a, b), (c, d, e)) = nested;

数组(Array)

数组是每个元素类型相同、长度固定的集合。

// 创建数组
let a = [1, 2, 3, 4, 5];
let months = ["January", "February", "March"];
 
// 指定类型和长度
let a: [i32; 5] = [1, 2, 3, 4, 5];
 
// 重复值初始化
let a = [3; 5];  // [3, 3, 3, 3, 3]
 
// 访问元素
let first = a[0];
let second = a[1];

数组越界:

let a = [1, 2, 3, 4, 5];
// let index = 10;
// let element = a[index];  // 运行时panic!

Rust会在运行时检查数组索引,越界访问会导致程序panic。

数组方法:

let mut arr = [3, 1, 4, 1, 5];
 
// 长度
println!("长度:{}", arr.len());
 
// 排序
arr.sort();
println!("排序后:{:?}", arr);
 
// 反转
arr.reverse();
 
// 填充
arr.fill(0);
 
// 查找
let pos = arr.iter().position(|&x| x == 0);

3.3 类型转换

Rust不允许隐式类型转换,必须使用as关键字显式转换。

as关键字

let x: i32 = 42;
let y: i64 = x as i64;  // i32转i64
 
let a: f64 = 3.14;
let b: i32 = a as i32;  // 浮点转整数,截断小数部分
 
let c: u8 = 255;
let d: i8 = c as i8;    // 溢出,变成-1

try_into转换(安全转换)

use std::convert::TryInto;
 
let a: i32 = 300;
let b: Result<u8, _> = a.try_into();
 
match b {
    Ok(v) => println!("转换成功:{}", v),
    Err(e) => println!("转换失败:{}", e),
}

3.4 类型推断

Rust编译器可以根据上下文推断变量类型:

let x = 5;              // 推断为i32
let y = 3.14;           // 推断为f64
let z = true;           // 推断为bool
let s = "hello";        // 推断为&str
let v = vec![1, 2, 3];  // 推断为Vec<i32>

显式标注的必要情况:

// 多种可能时
let guess: u32 = "42".parse().expect("不是数字!");
 
// 函数参数和返回值
fn add(x: i32, y: i32) -> i32 {
    x + y
}

3.5 类型别名

使用type关键字创建类型别名:

type Kilometers = i32;
type Thunk = Box<dyn Fn() + Send + 'static>;
 
let x: i32 = 5;
let y: Kilometers = 5;
 
println!("x + y = {}", x + y);  // 可以相加,因为是同一类型

3.6 Never类型(!)

Never类型表示永远不会返回的函数:

fn panic_now() -> ! {
    panic!("这个函数永远不会正常返回");
}
 
fn infinite_loop() -> ! {
    loop {
        println!("永远循环");
    }
}

练习题

练习题3.1:温度转换器

实现摄氏度、华氏度和开尔文温度的相互转换。

fn celsius_to_fahrenheit(c: f64) -> f64 {
    c * 9.0 / 5.0 + 32.0
}
 
fn fahrenheit_to_celsius(f: f64) -> f64 {
    (f - 32.0) * 5.0 / 9.0
}
 
fn celsius_to_kelvin(c: f64) -> f64 {
    c + 273.15
}
 
fn main() {
    let c = 100.0;
    println!("{}°C = {:.2}°F", c, celsius_to_fahrenheit(c));
    println!("{}°C = {:.2}K", c, celsius_to_kelvin(c));
}

练习题3.2:数组统计

计算数组的均值、最大值和最小值。

fn array_stats(arr: &[i32]) -> (f64, i32, i32) {
    if arr.is_empty() {
        return (0.0, 0, 0);
    }
 
    let sum: i32 = arr.iter().sum();
    let mean = sum as f64 / arr.len() as f64;
    let max = *arr.iter().max().unwrap();
    let min = *arr.iter().min().unwrap();
 
    (mean, max, min)
}
 
fn main() {
    let numbers = [23, 56, 12, 89, 34, 67, 45];
    let (mean, max, min) = array_stats(&numbers);
 
    println!("数组:{:?}", numbers);
    println!("平均值:{:.2}", mean);
    println!("最大值:{}", max);
    println!("最小值:{}", min);
}

练习题3.3:元组交换

交换元组中两个元素的位置。

fn swap_tuple<T, U>(t: (T, U)) -> (U, T) {
    (t.1, t.0)
}
 
fn main() {
    let pair = ("hello", 42);
    let swapped = swap_tuple(pair);
    println!("原元组:{:?}", pair);
    println!("交换后:{:?}", swapped);
}

练习题3.4:素数检查

检查一个数是否为素数。

fn is_prime(n: u32) -> bool {
    if n <= 1 {
        return false;
    }
    if n <= 3 {
        return true;
    }
    if n % 2 == 0 || n % 3 == 0 {
        return false;
    }
 
    let mut i = 5;
    while i * i <= n {
        if n % i == 0 || n % (i + 2) == 0 {
            return false;
        }
        i += 6;
    }
    true
}
 
fn main() {
    for n in 1..=100 {
        if is_prime(n) {
            print!("{} ", n);
        }
    }
    println!();
}

练习题3.5:进制转换

将十进制数转换为二进制、八进制和十六进制字符串。

fn to_binary(n: u32) -> String {
    format!("{:b}", n)
}
 
fn to_octal(n: u32) -> String {
    format!("{:o}", n)
}
 
fn to_hex(n: u32) -> String {
    format!("{:X}", n)
}
 
fn main() {
    let n = 255;
    println!("十进制:{}", n);
    println!("二进制:{}", to_binary(n));
    println!("八进制:{}", to_octal(n));
    println!("十六进制:{}", to_hex(n));
}

本章小结

本章详细介绍了Rust的基本数据类型:

掌握这些基本类型是编写Rust程序的基础。在下一章中,我们将学习Rust的控制流结构。