| 类型 | 说明 |
|---|---|
i8、i16、i32、i64、i128、u8、u16、u32、u64、u128 | 给定宽度的有 无符号整数,有符号整数 |
isize 、 usize | 与机器字(32位或者64位)一样大的有符号整数与无符号整数 |
f32、 f64 | 单精度 IEEE 浮点数和双精度 IEEE 浮点数 |
bool | 布尔值 |
char | Unicode字符,32位宽 (4字节) |
(char,u8,i32) | 元祖,允许混合类型 |
() | 单元 (空元祖) |
struct s {x:i32,y:i32} | 具有字短型结构体 |
struct T(i32,char) | 元祖型结构体 |
struct E; | 单元型结构体,无字段 |
enum Attend{ OnTime , Late(u32) } | 枚举或代数据类型 |
Box<Attend> | Box: 指向堆中的值拥有型指针 |
&i32 &mut i32 | 共享引用和可变引用: 非拥有型指针,其生命周期不能超过引用目标 |
String | utf-8 字符串,动态分配大小 |
&str | 对 str 的引用,指向UTF-8 文本中的非拥有型指针 |
[f64;4] [u8;256] | 数组,固定长度,其元素类型都相同 |
Vec<f64> | 向量, 可变长度,其元素类型都相同 |
&[u8] *mut [u8] | 对切片(数组或向量某一部分)的引用,包含指针和长度 |
Option(&str) | 可选值,或者为None(无值) 或者为Some(v)(有值,其值为v) |
Result<u64,Error> | 可能失败的操作结果,或者成功值 Ok(v),或者为错误值Err(e); |
&dyn Any &mut dyn Read; | 特定对象,是对任何实现了一组给定方法的值对引用 |
fn(&str)->bool | 函数指针 |
| (闭包类型没有显示书写形式) | 闭包 |
固定宽度的数值类型
| 大小(位) | 无符号整数 | 有符号整数 | 浮点数 |
|---|---|---|---|
| 8 | u8 | i8 | |
| 16 | u16 | i16 | |
| 32 | u32 | i32 | f32 |
| 64 | u64 | i64 | f64 |
| 128 | u128 | i128 | |
| 机器字 | usize | isize |
整型
无符号整型
| 类型 | 范围 |
|---|---|
| u8 | 0到 2^8-1 (0 到 255) |
| u16 | 0 到 2^16-1 (0 到 65 535) |
| u32 | 0 到 2^32-1 (0 到 4 294 967 295) |
| u64 | 0 到 2^64-1 (0 到 184 446 744 073 709 551 615 或 1844 京) |
| u128 | 0 到2^128-1 |
| usize | 0 到 2^32-1 或 2^64-1 |
有符号整型
| 类型 | 范围 |
|---|---|
| i8 | -2^7 到 2^7-1 |
| i16 | -2^15 到 2^15-1 |
| i32 | -2^31 到 2^31-1 |
| i64 | -2^63 到 2^63-1 |
| i128 | -2^127 到 2^127-1 |
| isize | -2^31 到 2^31-1 或 -2^63 到 2^63-1 |
// 0x 16 进制; 0o 8进制; 0b 二进制
let num = (0xffffff,0o71,0b110);
// 为了方便阅读 可以在任意数字之间插入下划线
let mum = 1_000_000_000u32;
数值类型和char 类型是不同的,但确实为 u8 提供了字节字面量.
// 与字符字面量类型 对应 ASCII 码 数字
let ch = b'X';
println!("{}",ch); /// 88
assert_eq!(b'A',65);
// 使用 as 运算符 将一种整形转化为另一种整形
assert_eq!(10_i8 as u16,10_u16);
assert_eq!(2525_u16 as i16,2525_i16);
assert_eq!(-1_i16 as i32,-1_i32);
assert_eq!(65535_u16 as i32,65535_i32);
// 超出目标范围到转化生成的值等于原始值对应2^N取模的值
// 其中N是按照位算的目标宽度
// 1000%256 取余
assert_eq!(1000_i16 as u8,232_u8);
// i32.pow(N) 取幂
assert_eq!(1000%(2_i32.pow(8)),232);
assert_eq!(65535_u32 as i16,-1_i16);
assert_eq!(-1_i8 as u8,255_u8);
assert_eq!(255_u8 as i8,-1_i8);
运算
assert_eq!(2_u16.pow(4),16); //求幂
assert_eq!((-4_i32).abs(),4); // 绝对值
// 0b101101_u8 8个字节 会补齐 形成 00101101 所以 count_ones count_zeros 都为4
// count_ones 求二进制1的个数
// count_zeros 求二进制0的个数
assert_eq!(0b101101_u8.count_zeros(),4); // 求二进制1的个数
println!("{}",0b11_u8.count_zeros()); //0b00000011 结果为 6
// 注意 方法的调用高于一元前缀运算符
assert_eq!(-4_i32.abs(),-4);
assert_eq!(04_i32.abs(),4);
当整形算术运算溢出时,在雕塑构建会出现panic,而在发布构建中运算会回绕: 生成的值等于“数学意义上正确的结果” 对“值类型范围”取模的值
整型算术方法分为4大类:
检查算法
检查算法(checked):会返回结果的 Option 值,如果数学意义上的结果可以表示该类型的值,那么就为Some(v) 否则为None
assert_eq!(10_u8.checked_add(20),Some(30));
assert_eq!(100_u8.checked_add(200),None);
// let sum = 100_u8.checked_add(200).unwrap(); // 溢出 则会出现panic
assert_eq!((-128_i8).checked_div(-1),None); // 结果为 128 超出了i8的表示范围,所以为None
回绕算法
回绕算法(wrapping) :返回的值等于“数学意义上正确的结果” 对“值类型范围”取模的值
assert_eq!(100_u16.wrapping_mul(200),20000);
assert_eq!(500_u16.wrapping_mul(500),53392);
// 对有符号类型的运算可能会回绕为负值;
assert_eq!(500_i16.wrapping_mul(500),-12144);
// 在位运算中,移位距离会在值大大小范围内回绕
// 所以在16位类型中移动17位就相当于移动了一位
assert_eq!(5_i16.wrapping_shl(17),10);
饱和运算
饱和运算(saturating) 会返回最接近“数学意义上正确结果” 的可表达值. 超过返回也只取范围内的最大值
assert_eq!(32760_i16.saturating_add(10),32767);
assert_eq!((-125_i8).saturating_sub(10),-128);
溢出运算
溢出运算(overflowing): 会返回一个元祖(result,overflowed),其中result 是函数的回绕版本所返回的内容.而overflowed 是一个布尔值,表示是否发生过溢出
assert_eq!(255_u8.overflowing_sub(2),(253,false));
assert_eq!(255_u8.overflowing_add(2),(1,true));
运算名称
| 类型 | 名称后缀 |
|---|---|
| add | 加法 |
| sub | 减法 |
| mul | 乘法; |
| div | 除法; |
| rem | 求余; |
| neg | 取负; |
| abs | 绝对值; |
| pow | 求幂; |
| shl | 按位左移动 |
| shr | 按位右移动 |
浮点类型
IEEE单精度浮点类型,和IEEE双精度浮点类型
这些类型包括了 正无穷大 和 负无穷大,不同的正零值 和 负零值,以及非数值.
| 类型 | 精度 |
|---|---|
| f32 | IEEE单精度 (至少6位小数) |
| f64 | IEEE双精度 (至少15位小数) |
println!("{}",f32::INFINITY); // 无穷大
println!("{}",f32::NEG_INFINITY); // 负无穷大
println!("{}",f32::NAN); // 非数值
println!("{}",f32::MAX); // 最大有限值
println!("{}",f32::MIN); // 最小有限值
println!("{}",5f32.sqrt()*5f32.sqrt()); // 双精度平方根
println!("{}",f32::sqrt(5f32)*f32::sqrt(5f32));
布尔类型 bool
- 布尔值具有次类型常用的两个值 true false;
- 比较运算符会生成 bool 结果;
- as 运算符可以将bool 转换为整型;
assert_eq!(false as u8,0);
assert_eq!(true as u8,1);
// 但是 as 无法进行另一个方向(从数值类型到bool)的转换
// 尽管bool 只需要用一个位来表示,但在内存中会使用整个字节来表示bool的值,因此可以创建指向它的指针
字符类型 char
字符类型char会以32位值表示单个unicode 字符
程序会对单独的字符使用char类型,但对字符串和文本流使用UTF-8编码 因此,String 会将其文本表示为UTF-8字节序列而不是字符数组
- 字符字面量是用单引号括起来的字符
- 如果字符的码点在 U+0000 到 U+007f 范围内(也就是它是从ASCII 字符集中提取的)
- char 总是包含0x0000 到 0xD7ff 或 0xE000 到 0x10FFFF 范围内到 Unicode 码点
- char 永远不会是 "半代用区" 中到码点(0xD800 到 0xDFFF 范围内到码点,它们不能单独使用) 或 Unicode 码点空间之外到值 (大于0x10FFFF)
char 不会 和任何类型之间进行隐形转换 可以使用as 转换运算符将char 转换为整型,对于小于32位到类型, 该字节到值到高位会被截断
assert_eq!('*' as i32,42);
assert_eq!('ಠ' as u16,0xca0);
assert_eq!('ಠ' as i8,-0x60);
// u8 是唯一能通过 as 运算转换位 char 的类型
// rust 刻意让 as 运算符只执行开销极低且可靠的转换
println!("{}", 0x60 as char)
元祖
元祖是各个类型值的值对
可以将元祖编写为一个元素序列,用逗号隔开并包裹在一对圆括号中
给定一个元祖t 可以通过 t.0 ,t.1 等访问其元素
rust 代码中常会用元祖类型中一个函数返回多个值
let text = "I see the eigenvalue in there eye";
let (head,tail) = text.split_at(21);
println!("{}",head);
println!("{}",tail);
// 另一种常用元祖 是零元祖() 传统上这种叫做单元类型.因为次类型只有一个值 ()
// 当无法携带任何有意义的值但其上下午仍然要求传入某个类型时.就使用单元元祖
rust 始终允许在所有能用逗号的地方(函数参数,数组,结构体和枚举定义...等等)添加额外的尾随逗号
(&str,i32,)
(&str,) 这里的逗号是必须的,用于区分单值元祖和简单的括号表达式
指针类型
程序需要让一些值指向其它值时,必须显示的使用指针类型
引用
&String 类型的值 是对String 值的引用 &i32 是对i32的引用 以此类推
引用的两种形式
- &T 不可变的共享引用 只读,禁止修改所指向的值
- &must T 可变的,独占的共享引用,可以读取和修改它指向的值,只要该值还存在,就不能对该值有任何类型的其它引用
Box
// 在堆中分配值 Box::new();
// 会分配足够的内存以在堆上容纳次元素组
let t = (12,"eggs");
let b = Box::new(t);
不安全指针(裸指针)
*mut T 和 *const T,
使用裸指针是不安全的,因为rust 不会跟踪它指向的内容
只能在 unsafe 块中对裸指针解引用
数组 向量 和 切片
数组 向量 和 切片 都表示内存中的序列值
数组
类型 [T;N] 表示N表示值的个数,每个值的类型为T,数组的大小在编译阶段已确定的常量,且是类型的一部分,不能修改数组大小
let lazy_caterer :[u32;6] = [1,2,4,7,11,16];
let taxonomy = ["Animalia","Arthropoda","Isecta"];
assert_eq!(lazy_caterer[3],7);
assert_eq!(taxonomy.len(),3);
let mut sieve = [true;10000];
for i in 2..100 {
if sieve[i]{
let mut j = i*i;
while j<10000 {
sieve[j] = false;
j+=i;
}
}
}
// println!("{:?}",sieve);
// 调用方法会隐式地对数组的引用转换为切片.可以在数组上调用任何切片的方法
let mut chaos = [3,5,4,1,2];
chaos.sort();
assert_eq!(chaos,[1,2,3,4,5]);
向量
类型
Vec<T>可称T的向量,是一个动态分配且可增长的T类型的值序列.存在余堆中,压入新元素,追加其它向量,删除元素等.
let mut primes = vec![2,3,5,7];
assert_eq!(primes.iter().product::<i32>(),210); // 所有元素相乘
primes.push(11);
primes.push(13);
assert_eq!(primes.iter().product::<i32>(),30030);
// 动态创建
fn new_pixel_buffer(rows:usize,cols:usize)->Vec<u8>{
vec![0;rows * cols]
}
// Vec::new 创建一个新的空向量
let mut pal = Vec::new();
pal.push("step");
pal.push("on");
pal.push("no");
pal.push("pets");
assert_eq!(pal,vec!["step","on","no","pets"]);
// 迭代器生成的值构建一个向量
// collect 通常要指定类型,因为它可以构建出不同种类的集合,而不仅仅是向量
let v = (0..5).collect::<Vec<i32>>();
assert_eq!(v,[0,1,2,3,4]);
// 可以使用切片的方法
// 回文
let mut palindrome = vec!["a man","a plan","a canal","panama"];
palindrome.reverse();
// Vec<T> 由3个值组成
// 指向元素在堆中分配的缓冲区的指针,缓冲区能够存储的元素数量(容量),以及它现在实际包含的数量(长度)
// 当缓冲区达到最大容量时,往向量中添加另一个元素需要分配一个更大的缓冲区,将当前内存复制到其中,更新向量指针和容量以指向新缓冲区,最后释放旧缓冲区
palindrome.len(); // 方法返回包含元素数
palindrome.capacity(); // 返回不重新分配内存的情况下可容纳的元素数
let mut arr = Vec::with_capacity(2);
assert_eq!(arr.len(),0);
assert_eq!(arr.capacity(),2);
arr.push(1);
arr.push(2);
assert_eq!(arr.len(),2);
assert_eq!(arr.capacity(),2);
arr.push(3);
assert_eq!(arr.len(),3);
println!("{}",arr.capacity());
// 在向量中任意位置插入元素和移除元素
let mut v= vec![10,20,30,40,50];
v.insert(3,35); // 下标对应元素后插入
assert_eq!(v,vec![10,20,30,35,40,50]);
v.remove(1); // 移除 下标
assert_eq!(v,vec![10,30,35,40,50]);
// 使用pop移除最后一个元素并将其返回Option<T>;
assert_eq!( v.pop(),Some(50));
assert_eq!( v.pop(),Some(40));
assert_eq!( v.pop(),Some(35));
assert_eq!( v.pop(),Some(30));
assert_eq!( v.pop(),Some(10));
assert_eq!( v.pop(),None);
// for循环变量向量
for i in &palindrome{
println!("值:{}",*i)
}
for (i,item) in palindrome.iter().enumerate() { // 带下标的循环
println!("下标:{} 值:{}",i,item)
}
切片
- 类型
&[T]和&mut [T]称为 T 的共享切片和 T 的可变切片,是对一系列元素的引用,这些元素是某个其它值(数组或向量)的一部分. - 可以将切片视为指向其第一个元素的指针,以及从该点开始允许访问的元素数量的计数;
- 切片总是通过引用传递
let v:Vec<f64> = vec![0.0,0.707,1.0,0.707];
let a:[f64;4] = [0.0,0.707,1.0,0.707];
let sv:&[f64] = &v;
let sa:&[f64] = &a;
// 普通引用指向单个值的非拥有型指针,而对切片的引用是指指向内存中一系列连续的非拥有型指针
fn print(n:&[f64]){
for e in n {
println!("{}",e);
}
}
print(sv);
print(sa);
// 可以使用范围值对数组或向量进行索引,以获取一个切片对引用,该引用既可以指向数组或向量也可以指向一个既有切片
print(&v[0..2]);
print(&a[2..]);
print(&sv[1..3]);
// 由于切片几乎总是出现在引用符号之后,因此通常只将&[T] 或 &str 只类对类型称为切片
println!("end");
字符串
字符串字面量
// 字符串字面量要用双引号括起来
let speech = "\"Ouch!\" said the well.\n";
// 跨行,换行符也是字符串对一部分,所以也会包含在输出中
println!("in the room the women come ad go,
Singing of Mount Abora");
// 原始字符串:用小写字母r进行标记.
let default_win_install_path = r"c:\Program Files\Gorillas";
let pattern = Regex::new(r"\d+(\.\d+)*");
// 在原始字符串的开头和结尾加#标记
// 根据需要添加任意多个#号,以标明原始字符串起止位置 可以规避一些转义
println!(r###"
this raw string started with 'r###'
"###);
字符串
/// 带b前缀的字符串字面量都是字节串 这样的字节串是U8值(字节)的切片而不是Unicode文本
let method = b"get";
assert_eq!(method,&[b'g',b'e',b't']);
// 字节串不能包含任意Unicode 字符,它们只能使用 ASCII 和 \xHH 转义序列
内存中的字符串
// 字符串是Unicode字符序列,并没有以char数组的形式存储在内存中,而是使用了UTF-8的形式
let noodles = "noodles".to_string();
let oddles = &noodles[1..];
let poodles = "ಠ_ಠ";
string &str 和 str 的区别
- string: 有一个可以调整大小的缓冲区,其中包含了UTF-8文本.缓冲区是在堆中分配的
- &str: 是对拥有一系列UTF-8 文本的引用,即”借用“了整个文本
- str: 字符串字面量是指分配文本中的 &str 它通常与程序的机器码一起存储在只读存区
// String 或 &str 的len() 方法会返回其长度,长度以字节而不是字符
assert_eq!("ಠ_ಠ".len(),7);
assert_eq!("ಠ_ಠ".chars().count(),3);
// 不能修改 &str
let mut s = "hello";
// s[0] = 'c';
// s.push('\n')
// &mut str 上唯一可以操作的是
// s.make_ascii_lowercase();
// s.make_ascii_uppercase();
string
&str 非常像&[T] 是指向某些数据的胖指针
String 类似于Vec<T>
| Vec | String | |
|---|---|---|
| 自动释放缓冲区 | 是 | 是 |
| 可增长 | 是 | 是 |
| 类型关联函数 ::new() 和 ::with_capacity() | 是 | 是 |
| .reserve() 方法和 .capacity() 方法 | 是 | 是 |
| .push()方法和pop方法 | 是 | 是 |
| 范围语法v[start..stop] | 是,返回 &[T] | 是,返回&str |
| 自动转换 | &Vec<T> 到 &[T] | String 到 &str |
| 继承方法 | 来自&[T] | 来自&str |
// 创建String
/// to_string 和 to_owned() 将&str 转换为 String ,复制此字符串
let error_message = "too many pets".to_string();
let error_message = "too many pets".to_owned();
// format!() 宏的工作方式与println!()类似.但它会返回新的String
assert_eq!(format!("{}{}{}",2,3,4),"234".to_string());
// 字符串数组,切片和向量都有两个方法 concat() 和 join(sep),重字符串中生成新的String
let bits = vec!{"veni","vidi","vici"};
assert_eq!(bits.concat(),"venividivici");
assert_eq!(bits.join(","),"veni,vidi,vici");
// &str 可以引用任意字符串的任意切片,无论是字符串字面量还是String.
// 如果希望允许调用者传递任何一种字符串,那么&str 更合适作为函数参数
字符串的使用
// 字符串支持 == 运算符和 != 运算符
// 如果两个字符串以相同的序列包含相同的字符(无论是否指向内存中相同位置),则认为它们是相等的
assert!("ONE".to_lowercase()== "one");
// 字符串还支持比较运算符 < <= 和 >=
assert!("peanut".contains("nut"));
assert_eq!("ಠ_ಠ".replace("ಠ","*"),"*_*");
assert_eq!(" clean \n".trim(),"clean");
for word in "veni, vidi, vici".split(", ") {
assert!(word.starts_with("v"));
}
其它类似字符串的类型
-
对Unicode 文本,坚持使用 String 和 &str
-
当使用文件名时,改用std::path::PathBuf 和 &Path.
-
当处理根部不是UTF-8编码的二进制数据时,请使用 Vec<8> 和&[u8]
-
当使用操作系统提供和原生形式的环境变量命名和命令行参数时, 使用OsString 和 &OsStr.
-
当使用null 结尾字符串的C语言库进行互操作时,请使用std::ffi:CString 和 &Cstr
类型别名
// 可以使用关键字来为现有类型声明一个新名称
type Bytes = Vec<u8>;