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 types和trait.
这节我们将探讨rust中表示值的基础类型;不像动态类型,我们必须显式给出函数参数及返回值、结构体的字段以及一些其他结构的类型;除此之外rust提供以下两个机制来改进类型标注的体验:
- 在标注需要的类型之后,rust会使用类型推导的机制推断出其他类型,例如我们可以只标注下面函数的返回类型
fn build_vector() -> Vec<i16> {
let mut v = Vec::new();
v.push(10);
v
}
- 函数可以是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,如果结果不正确返回Nonewrapping
: 返回数值正确的值,会进行取模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_lowercase
和 make_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>;