rust 教程 01 基础类型

rust 教程 01

安装

我们通常使用rustup来安装rust的相关开发工具,默认存储在~/.cargo/bin目录下,使用如下命令即可安装rust:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# update rustup
rustup update

基础类型

rust的内存和线程安全依赖其类型系统的soundness,rust的灵活性依赖于generic typestrait.

这节我们将探讨rust中表示值的基础类型;不像动态类型,我们必须显式给出函数参数及返回值、结构体的字段以及一些其他结构的类型;除此之外rust提供以下两个机制来改进类型标注的体验:

  1. 在标注需要的类型之后,rust会使用类型推导的机制推断出其他类型,例如我们可以只标注下面函数的返回类型
fn build_vector() -> Vec<i16> {
    let mut v = Vec::new();
    v.push(10);
    v
}
  1. 函数可以是generic的

我们在这里给出类型概览的表格:

类型 描述
i/u8, i/u16, i32, i64, i128 有符号与无符号数 42, -5i8, 20_922u64
isize / usize 与机器地址相同长度的数 137
f32/f64 3.14f32
bool true, false
char '*'
(char, u8, i32) 元组 ('%', 0x7f, -1)
() unit类型 ()
struct S 具名结构体 S
struct T (i32,char); 类元组结构体 T(120, 'x')
struct E; 没有field E
enum Attend 枚举类型 Attend::Late(5), Attend::OnTime
Box <Attend> Box: 拥有一个指向堆内存的指针 Box::new(Attend::Late(5))
&i32, &mut 32 共享以及可变引用,没有所有权 &s.y, &mut v
String 动态长度的字符串 "hello".to_string()
&str 指向str的引用,没有所有权 "hello", &s[..]
[f64;4] 固定长度的数组 [2.0, 2.0, 1.0, 3.0]
Vec 变长数组 vec![0.1, 0.2]
&[u8], &mut [u8] 指向切片的引用, 是一个胖指针,包括指针以及长度 &v[..]
Option<&str> 可选值
Result<u64, Error> Ok(v) or Err(e)
&dyn Any, &mut dyn Read trait 对象: 也是一个引用,主要是动态分发 value as &dyn Any
fn(&str) -> bool 指向函数的指针 str::is_empty
闭包类型 Closure |a, b|

我们将在这里以及之后的博客提供这些类型更多的细节。

定长的数值类型

如果你需要特殊的精度,可以查看 num crate 找到需要的功能。

整型

我们使用u8来作为字节类型,例如读取二进制文件等。注意char和u8是两个不同的类型。

rust的usize和isize类似于c的size_t以及c++的ptrdiff_t, 注意rust中的数组索引为 usize 类型,array vec等的长度也通常是usize类型。

整型字面量可以有后缀,例如42u8等;如果未指定,rust会自动推断出类型,如果有多个可能的候选类型,并且i32也是候选之一,那么将其指定为i32类型。

前缀主要指16,8,2进制, 0x 0o 0b。我们可以使用下划线来分割数字,使其更可读。

提供一个char-like的byte字面量: b'X'; 有些需要通过 \来进行转义。除此之外,你可以使用16进制的写法增加可读性,例如 b'\xHH'.

可以通过使用 as 将一个整数类型转为另一个;注意可能发生截断和补齐。

标准库(包括i32以及std::i32)也提供了一些整型上的操作,例如

assert_eq!(2_u16.pow(4), 16);
// .abs()
// .count_ones()

通常来说,在调用这些方法时,我们不需要加上后缀;但是有些情况比较特殊,

println!("{}", (-4).abs());

这里编译器将会报错,默认的i32类型只有在所有其他方法都被解析完成后才能生效。解决方法:

(-4_i32).abs()
// or i32::abs(-4) 

同时也需注意函数调用相比单目运算符具有更高的优先级,所以上面的例子需使用括号包裹。

checked, wrapping, saturating, and overflow

注意数值运算可能发生益处,在debug模式下,会使得程序panic;release模式将会像c/c++一样

我们可以显式指定在任意build下的行为:

loop {
    // 在任意build下,溢出都会panic
    i = i.checked_mul(10).expect("overflow!"); 
}
  • checked: 返回结果的option,如果结果不正确返回None
  • wrapping: 返回数值正确的值,会进行取模
  • staturating: 返回最接近的值,最大或最小
  • overflowing: 返回一个元组 (result, overflowed), result 是wrapping的结果,overflowed指定是否发生了溢出

浮点

rust可能使用 f64 作为默认类型,注意只有浮点字面量才能推导出浮点类型;注意f32 和 f64都有关联常量: INFINITY NEG_INFINITY NAN MIN MAX

std::f32::consts std::f64::consts 提供了常见的数学常量,如 pi

bool 类型

注意只能由bool值转为整型值;反方向将不能。虽然bool逻辑上只需要一个bit,但是实际存储时也是存一个byte,所以可以创建指向它的指针。

char

char类型表示一个unicode字符,32-bit。但需注意String表示的是UTF-8的字节序列,与单个char有区别。

  • 如果char的code point在ascii字符范围内,可以使用类似 '\x2A'来表示
  • 我们也可以写'\u{HHHHHH}'来表示任意的unicode

只能使用as将其强制转为其他整型;只有u8可以被转为char;但是标准库也提供一些函数 std::char::from_u32: 返回一个Option

tuples

给定一个tuple的值,可以通过类似t.0访问对应的值;注意tuple只能使用常量作为索引值。

注意单元组需要使用(&str,)进行声明;rust在很多地方都运行末尾多余的逗号:函数参数,数组,结构体,枚举等的定义。

指针类型

三种:ref, box, raw pointers

ref

表达式&x会产生指向x的引用;在rust的术语下,我们称借用x的一个引用;*r会解包出对应的值。存在两种类型:

  • &T: 不可变,共享的引用
  • &mut T: 可变 专用的引用

Boxes

在堆上分配一个值,使用Box::new方法:

let t = 12;
let b = Box::new(t);

当b离开作用域时,b的资源将被释放;除非b被移动,例如返回它

raw pointers

*mut T *const T, 使用raw pointer是不安全的。注意只能在unsafe中deref raw pointers。

Array vector slices

  • array: [T; N]
  • vec: Vec<T>
  • slices: &[T] &mut[T]

注意索引只能为usize类型;我们可以slice array, vector甚至继续slice slice。比如

fn main() {
    let s = vec![1,2,3,4,5];
    let s_1 = &s[1..3];
    let s_2 = &s_1[1..];
    println!("{:?}", s_1);
    println!("{:?}", s_2);
    println!("{:?}", s);
}

String 类型

类似于C++中两类string 类型,一种字面量的类型为const char *;另一种则可以在运行时进行动态更改的std::string.

string 字面量

使用双引号包裹,并可以使用\进行转义,可以是多行,其中的空白字符将不会被strip,如果在一行的结尾使用\时,空白字符将被strip;注意我们可以使用raw string,比如r"C:\"以及r###" "###

byte strings

带有b前缀的字符字面量;这种string是u8的slice:

let method = b"GET";

注意它不具有我们将讨论的string 的任何方法

内存中的strings

rust的strings是unicode char的序列,但在内存中,它们并不是作为chars的数组进行存储;相反它使用utf-8进行存储,这是一种变长的编码形式;每个ascii字符只存一个byte,其他则占据多个byte。

String&str.len()方法都返回它的长度,这个长度指的是bytes数,而不是char的数目。在运行时创建新的string那就使用String;注意&mut str并不是非常有用,因为对于切片元素的更改,可能会导致字符大小的更改,而切片不能重新分配内存,唯一有用的方法是 make_ascii_lowercasemake_ascii_uppercase

String

有一些创建 String 的方法:

  • to_string:可以将&str转为String
  • format!(): 例如 format!("{}, {}", 10, 20)
  • .concat() .join(seq): 主要是对于strings的arr vec slice使用

使用String

支持==!=两种运算。

其他string-like的类型

  • 文件名 std::path::PathBuf&Path
  • 环境变量 命令行参数 OsString &OsStr
  • 与C库进行交互时, std::ffi::CString &CStr

类型别名

type Bytes = Vec<u8>;

posted @ 2025-03-21 16:30  xwher  阅读(102)  评论(0)    收藏  举报