基本数据类型

1,166 阅读16分钟
类型说明
i8i16i32i64i128u8u16u32u64u128给定宽度的有 无符号整数,有符号整数
isizeusize与机器字(32位或者64位)一样大的有符号整数与无符号整数
f32f64单精度 IEEE 浮点数和双精度 IEEE 浮点数
bool布尔值
charUnicode字符,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共享引用和可变引用: 非拥有型指针,其生命周期不能超过引用目标
Stringutf-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函数指针
(闭包类型没有显示书写形式)闭包

固定宽度的数值类型

大小(位)无符号整数有符号整数浮点数
8u8i8
16u16i16
32u32i32f32
64u64i64f64
128u128i128
机器字usizeisize

整型

无符号整型

类型范围
u80到 2^8-1 (0 到 255)
u160 到 2^16-1 (0 到 65 535)
u320 到 2^32-1 (0 到 4 294 967 295)
u640 到 2^64-1 (0 到 184 446 744 073 709 551 615 或 1844 京)
u1280 到2^128-1
usize0 到 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双精度浮点类型

这些类型包括了 正无穷大 和 负无穷大,不同的正零值 和 负零值,以及非数值.

类型精度
f32IEEE单精度 (至少6位小数)
f64IEEE双精度 (至少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>

VecString
自动释放缓冲区
可增长
类型关联函数 ::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>;