【Rust笔记】01-基本类型

01 - 基本类型

1.1 - 机器类型

1.1.1 - 整数类型

  • 无符号整数
类型范围
u80 到 2^8-1(0 ~ 255)
u160 到 2^16-1(0~65535)
u320 到 2^32-1
u640 到 2^64-1
u1280 到 2^128-1
  • 有符号整数
类型范围
i8-2^7 到 2^7-1(-128 ~ 127)
i16-2^15 到 2^15-1(-32768 ~ 32767)
i32-2^31 到 2^31-1
i64-2^63 到 2^63-1
i128-2^127 到 2^127-1
  • usize 和 isize 机器字

    • usize 表示无符号,范围是 0 到 2^32-1,或者是 0 到 2^64-1。
    • isize 表示有符号,范围是 - 2^31 到 2^31-1,或者是 - 2^63 到 2^63-1。
    • 精度取决于机器的寻址空间大小,32 位机器是 32 位长,64 位机器是 64 位长。
    • 数组索引必须是 usize 值。
    • 数组或向量的大小,或者某些数据结构中的元素数量的值,也是 usize 值。
  • 整数字面量

    • 格式:进制标识 + 数值 + 类型

      116i8 		// 类型为i8,十进制数为116
      0xcafeu32 	// 类型为u32,十六进制数为cafe,十进制数为51966
      0b0010_1010 // 类型自推断,二进制数为0010 1010,十进制数为42
      0o106		// 类型自推断,八进制数为106,十进制数为70
      
    • 长数值可以在数字之间插入下划线

      • 十进制数每 3 未插入一个下划线:u32 最大数可以写为:4_294_967_295;
      • 十六进制或二进制每 4 位插入一个下划线:0xffff_ffff;
      • 数字与类型后缀可以用下划线分开:127_u8
  • 字节字面量

    • 用类字符字面量表示的 u8 值;
    • 字节字面量智能出现在 ASCII 编码的字符。
  • 需要转义的字符

    • 对于难写或难读的字符,可以用十六进制写出它们的编码。如 b'\xHH'
    字符字符字面量u8 数值
    单引号(')b'\''39u8
    反斜杠(\)b'\\'92u8
    换行b'\n'10u8
    回车b'\r'13u8
    制表符b'\t'9u8
  • 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 是目标类型的位宽。

      assert_eq!(1000_i16 as u8, 232_u8);
      assert_eq!(65535_u32 as i16, -1_i16);
      assert_eq!(-1_i8 as u8, 255_u8);
      assert_eq!(255_u8 as i8, -1_i8);
      

1.1.2 - 浮点类型

  • 浮点类型

    类型精度范围
    f32IEEE 单精度(至少 6 位小数)约 - 3.4×10^38 到 3.4×10^38
    f64IEEE 双精度(至少 15 位小数)约 - 1.8×10^308 到 1.8×10^308
    • 遵循 IEEE 754-2008 标准。
    • 包含正、负无穷;
    • 区分正、负零;
    • 包含非数值(not-a-number)值
  • 浮点字面量

    • 整数部分 . 小数部分. e-4(指数部分) f64(类型后缀)
    • 小数部分、指数和类型后缀三者至少要有一个存在
    • 小数部分也可以只有 1 个小数点,如 5.
    • 根据上下文推断,可以推断数值的类型。
    • Rust 将整数字面量和浮点字面量看成不同的类型。
  • 常量特殊值

    • std::f32std::f64 定义;
    • 正无穷:INFINITY
    • 负无穷:NEG_INFINITY
    • 非数值(Not A Number):NAN
    • 最小值:MIN
    • 最大值:MAX
  • 数学常量

    • std::f32::constsstd::f64::consts 定义;
    • E、PI 等常用数学常量;
    • 2 的平方根:2f63.sqrt()

1.1.3 - 布尔类型

  • 布尔类型 bool 的值:

    • true
    • false
    • ==>< 等比较操作符都可以产生布尔值
  • ifwhile 等控制结构,要求它们的条件必须是 bool 表达式

    • if x {...} 不正确;
    • 必须写成 if x != 0 {...} 才可。
  • 短逻辑操作符 &&|| 同样要求它们的条件必须是 bool 表达式

  • as 操作符(功能二)

    • 可以把 bool 值转换为整数类型。

      assert_eq!(false as i32, 0);      // false转换为0
      assert_eq!(true as i32, 1);       // true转换为1
      
    • 但不可以把整数类型转换为 bool 值。

  • Rust 在内存里使用一个字节作为 bool 值,因此可以创建一个指向它的指针。

1.1.4 - 字符类型

  • 字符类型 char

    • 以 32 位值的形式,表示单个 Unicode 字符。
  • 字符串类型 String

    • 字符串或文本流则使用 UTF-8 编码。
    • 将文本表示为一个 UTF-8 的序列,而不是字符的数组。
  • 字符字面量:

    • 单引号引起来的字符,如'8''!'

    • 需要转义的字符:

      字符字符字面量
      单引号(')b'\''
      反斜杠(\)b'\\'
      换行b'\n'
      回车b'\r'
      制表符b'\t'
  • 可以在单引号里写出相应字符的十六进制 Unicode 码点:

    码点范围字符形式
    U+0000U+007F(ASCII 字符集)'\xFF''*' 等于'\x2A'
    任何 Unicode 字符\uFFFFFF,使用 1 至 6 位十六进制数
    0x00000xD7FF,或 0xE0000x10FFFFchar 类型保存的码点范围
  • as 操作符(功能三):

    • 把 char 类型转换为整数类型。

    • 如果转换的整数类型小于 32 位,则字符值的高位(upper bits)会被截短。

      assert_eq!('*' as i32, 42);
      
    • u8 是唯一可以转换为 char 类型的整数类型。

  • std::char::from_u32 可以将 u32 值转换为 Option<char>

    • 有效会返回 Some(c),其中 c 就是转换后的 char 值。
    • 无效则返回 None

1.2 - 元组

  • 元组(tuple):

    • 格式为:("Brazil", 1985)

    • 每个元素都可以是一个不同的类型。相反,数组的元素必须是相同的类型

    • 元组只允许用常量作为索引。数组索引是 usize 值

    • 可以使用元组来返回多个值:

      fn split_at(&self, mid: usize) -> (&str, &srt);
      
      // 举例
      let text = "I see the eigenvalue in thine eye";
      let temp = text.split_at(21);
      let head = temp.0;
      let tail = temp.1;
      assert_eq!(head, "I see the eigenvalue ");
      assert_eq!(tail, "in thine eye");
      
  • 基元类型

    • 零元组 ();
    • 没有返回值的函数的返回类型就是基元。如 std::mem::swap
  • 允许在函数参数、数组、结构体、枚举等任何使用逗号的地方的末尾再加一个逗号。

    • (1,) 表示只有一个元素的元组,末尾的逗号是必须的

1.3 - 指针

Rust 使用指针时,必须显示地使用指针类型。

1.3.1 - 引用

  • 可以在堆、栈上分配内存。
  • &x 借用了一个对 x 的引用。
  • *r 引用的则是 r 指向的值。
  • 引用永远不会是空值。以下错误在编译时会被发现:
    • 悬空指针
    • 重复释放
    • 指针失效
  • 引用默认是不可修改的:
    • &T:不可修改引用,类似 C 中的 const T*
    • &mut T:可修改引用,类似 C 中的 T*

1.3.2-Box

  • Box::new() 只可以在堆上分配内存。
  • 转移,处理分配到堆上的值。

1.3.3 - 原始指针

  • 原始指针是不安全的指针类型,C/C++ 中的指针:
    • *const T:不可修改指针。
    • *mut T:可修改指针。
  • 原始指针解引用,一般只能在 unsafe 块中进行。

1.4 - 序列

1.4.1 - 数组

  • 类型 [T; N]

    • 表示一个 N 个值的数组,每个值的类型都是 T
    • 数组的大小是在编译时确定的常量,也是类型自身的一部分。
    • 不能向数组中追加新元素,也不能缩短数组。
  • 支持索引:

    • v.len(),返回序列 v 的元素数量;
    • v.[i],引用的是序列 v 的第 i 个元素,i 必须是 usize 值;
    • 第一个元素是 v[0]
    • 最后一个元素是 v[v.len() - 1]
  • 语法:

    • 把一系列逗号分隔的值,用方括号括起来:

      let lazy_caterer: [u32; 6] = [1, 2, 4, 7, 11, 16];
      let taxonomy = ["Ainimalia", "Arthropoda", "Insecta"];
      
      assert_eq!(lazy_caterer[3], 7);
      assert_eq!(taxonomy.len(), 3);
      
    • 数组字面量 [V; N]V 是每个元素的值,N 是数组的长度。

      [0u8; 1024] 表示固定大小(1KB)的缓冲区,以 0 字节填充。

    • 没有语法表示未初始化的数组

  • 可以直接在数组上调用切片的任何方法。

1.4.2 - 向量

  • 类型 Vec<T>,被称为 T 类型的向量:

    • 一种动态分配、可扩展的类型 T 的值序列。
    • 向量的元素保存在堆上,因此可以随意缩放。
    • 支持追加新元素,追加其他向量,或者删除元素等操作。
  • 支持索引:

    • v.len(),返回序列 v 的元素数量;
    • v.[i],引用的是序列 v 的第 i 个元素,i 必须是 usize 值;
    • 第一个元素是 v[0]
    • 最后一个元素是 v[v.len() - 1]
  • 创建向量:

    • vec! 宏:

      let mut v = vec![2, 3, 5, 7];
      v.push(11);           //可以动态向其中添加元素
      
    • 可以模仿数组字面量的语法,讲一个值重复一定次数来构建向量:

      fn new_pixel_buffer(rows: usize, cols: usize) -> Vec<u8> {
          vec![0; rowl * cols]             // 没有用分号结尾,表示返回值
      }
      
    • 可以基于迭代器产生的值来构建向量:

      let v: Vec<i32> = (0..5).collect();
      assert_eq!(v, [0, 1, 2, 3, 4]);
      
    • Vec::new 创建向量【不推荐使用】:

      • 当缓冲区达到容量上限后,再给向量添加元素会导致一系列操作:

        分配一个更大的缓冲区,将现有的内容复制过去,基于新缓冲区更新向量的指针和容量,最后释放旧缓冲区。

    • Vec::with_capacity 创建向量【推荐使用】:

      • 提前知道向量中需要保存的元素个数先创建一个足够大的缓冲区,存储所有元素的向量再逐个向向量中添加元素,从而避免内存的重新分配。

      • capacity 方法举例:

        let mut v = Vec::with_capacity(2);
        assert_eq!(v.len(), 0);        // 向量的长度为0
        assert_eq!(v.capacity(), 2);   // 无需重新分配,即可包含的元素个数
        
        v.push(1);
        v.push(2);
        assert_eq!(v.len(), 2);
        assert_eq!(v.capacity(), 2);
        
        v.push(3);
        assert_eq!(v.len(), 3);
        assert_eq!(v.capacity(), 4);
        
  • 与数组一样,对向量也可以使用切片方法。

  • 一个 Vec<T> 包含 3 个值:

    • 缓冲区的引用;
    • 该缓冲区可以存储的元素个数(即容量);
    • 实际存储的元素个数(即长度)。
  • 可以在向量的任何位置,随意插入或删除元素,对于长向量来说,这些操作比较慢:

    • v.insert(3, 35):在索引 3 处插入 35;
    • v.remove(1):删除索引 1 处的元素;
    • v.pop():删除并返回最后一个元素,如果向量为空,则返回 None。
  • 使用 for 循环可以迭代遍历向量:

    let languages: Vec<String> = std::env::args().skip(1).collect();
    
    for i in languages {
        println!("{}: {}", i,
            if i.len() % 2 == 0 {
                "functional"
            } else {
                imperative
            });
    }
    

1.4.3 - 逐个元素地构建向量

  • 以每次添加一个元素的方式,构建向量。
  • 实际元素的个数,至少是缓冲区大小的一半。
  • 对于较小的向量来说,Vec::with_capacity 的实现速度比 Vec::new 块。

1.4.4 - 切片(slice)

  • 类型 &[T],被称为 T 类型的共享切片;本质为对切片的引用。

  • 类型 &mut [T],被称为 T 类型的可修改切片;

  • 他们是对其他值(如数组或向量)中部分元素序列的引用:

    • 切片等同于指针:包含切片中第一个元素的地址,和从这个元素起可以访问元素的数量。
    • &mut [T] 允许读写元素,但不能共享;
    • &[T] 可以被多个读者读取,但不能修改元素。
  • 支持索引:

    • v.len(),返回序列 v 的元素数量;
    • v.[i],引用的是序列 v 的第 i 个元素,i 必须是 usize 值;
    • 第一个元素是 v[0]
    • 最后一个元素是 v[v.len() - 1]
  • 切片可以是任意长度:

    • 不能直接保存到变量中;
    • 也不能作为函数参数传递;
    • 永远只能按引用传递。
  • 切片的引用是一个胖指针(fat pointer),双字宽的值,保存如下内容:

    • 指向切片第一个元素的指针
    • 切片中元素的个数
  • 普通引用与切片引用的不同:

    • 普通引用是对单个值的非所有型指针;

    • 切片引用是对多个值的非所有型指针。(切片应用适合操作一串同类数据的函数,这样的数据不管是存储在数组、向量,还是栈、堆上)

      let v: Vec<f64> = vec![0.0, 0.707, 1.0, 0.707];
      let a:
      
      fn print(n: &[f64]) {
          for elt in n {
              println!("{}", elt);
          }
      }
      print(&v);
      print(&a);
      
  • 在索引中使用范围,可以获取对数组或向量切片的引用,或者对现有切片的切片的引用

    print(&v[0.. 2]);  // 打印v的前两个元素
    print(&a[2..]);    // 从a[2]开始打印a的元素
    print(&sv[1.. 3]); // 打印v[1]和v[2]
    

1.5 - 字符串

1.5.1 - 字符串字面量

  • 使用双引号引起来的字符串,就是字符串字面量。

  • 在字符串字面量中,单引号无须转义,双引号需要转义。

  • 使用技巧:

    • 字符串可以分散到多行,换行符和空白符都包含在字符串中,同样会被输出。
    • 如多行文本以反斜杠结尾,那么换行符、空白符都会被删除。
  • 原始字符串

    • 以小写字母 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###"'.
          Therefore it does
          "###);
      

1.5.2 - 字节字符串

  • 字节字符串使用前缀 b 标记的字符串字面量,是 u8 值(字节)的切片
  • 原始字节字符串使用前缀 br 标记
  • 字节字符串不能包含任意 Unicode 字符,只能是 ASCII 和 \xFF 转义序列

1.5.3 - 字符串

  • 字符串在内存中的表示

    • 字符串在内存中使用 UTF-8 可变宽编码存储。
    • 字符串中每个 ASCII 字符占用 1 个字节,其他字符占用多个字节。
    • String 字符串类型:在可伸缩的缓冲区上存储 UTF-8 文本,分配在堆上,可按需求或请求来调整大小。
    • &str 字符串切片类型:是对其他 UTF-8 文本的引用,为胖指针(&[u8]),包含实际数据的地址及其长度。
    • "abcd" 字符串字面量:是一个引用预分配文本的 &str,与程序的机器码一期存储在只读内存中。
  • 字符串

    • &str 等价于 &[T]

      • 可以引用任何字符串的任意切片。
      • 适合作为函数的参数。
    • String 等价于 Vec<T>

    • 创建 String 的方法:

      • .to_string() 方法:把 &str 转换为 String

        let error_message = "too many pets".to_string();
        
      • format!() 宏:返回一个新的 String

        assert_eq!(format!("{}时{:02}分{}秒", 21, 5, 23)"21时05分23秒".to_string());
        
      • 字符串数组、切片和向量的.concat().join(sep) 方法,可将多个字符串拼接成一个新的 String

        let bits = vec!["veni", "vidi", "vici"];
        assert_eq!(bits.concat(), "venividivici");
        assert_eq!(bits.join(", "), "veni, vidi, vici");
        
  • 使用字符串

    • 字符串支持 ==!= 操作符:当两个字符串包含的字符相同、顺序也相同,那么他们就是相等的。

      assert_eq!("ONE".to_lowercase() == "one");
      
    • 字符串也支持比较操作符 <<=>>=

1.5.4 - 其他类似字符串的类型

  • 对于 Unicode 文本,使用 String&str
  • 处理文件名时,使用 std::path::PathBuf&Path
  • 处理根本不是字符数据的二进制数据时,使用 Vec<u8>&[u8]
  • 处理以操作系统原生形式表示的环境变量名和命令行参数时,使用 OsString&OsStr
  • 与使用空字符结尾字符串的 C 库互操作时,使用 std::ffi::CString&CStr

详见《Rust 程序设计》(吉姆 - 布兰迪、贾森 - 奥伦多夫著,李松峰译)第三章
链接地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

phial03

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值