0
点赞
收藏
分享

微信扫一扫

ASP.NET Core 标识(Identity)框架系列(二):使用标识(Identity)框架生成 JWT Token

禾木瞎写 04-14 22:00 阅读 1

Rust 是静态编译语言,在编译时必须知道所有变量的类型。

基于使用的值,编译器通常能推断出它的具体类型,但如果可能的类型比较多,例如把String转换为整数的parse方法,就必须添加类型的标注,否则编译会报错。

//报错:程序不知道把字符串转换成啥类型
let guess = "42".parse().expect("Not a number");
//正确:程序知道要转换成u32类型
let guess:u32 = "42".parse().expect("Not a number");

在rust中,每个值都有其确切的数据类型,总的来说可以分为两类:标量类型和复合类型。

一个标量类型意味着它代表的是一个单个的值,rust有三个主要的标量类型:

  • 数值类型: 有符号整数 (i8, i16, i32, i64, isize)、 无符号整数 (u8, u16, u32, u64, usize) 、浮点数 (f32, f64)
  • 布尔类型: true和false
  • 字符类型: 表示单个 Unicode 字符,存储为 4 个字节

复合类型,顾名思义,是指由多个标量类型的值和其他符号组合而成的数据类型

Rust内置的复合类型有:元组、数组。

一、标量类型

(一)整数类型

比如 i32 类型(rust默认的整数值类型),表示的就是有符号的 32 位整数类型( i 是英文单词 integer 的首字母,与之相反的是 u,代表无符号 unsigned 类型)。下表显示了 Rust 中的内置的整数类型:

长度(视架构而定)有符号类型无符号类型
8 位i8u8
16 位i16u16
32 位i32u32
64 位i64u64
arch(架构位数,如128)isize(i128)usize(u128)

类型定义的形式统一为:有无符号 + 类型大小(位数)。无符号数表示数字只能取正数和0,而有符号则表示数字可以取正数、负数还有0。就像在纸上写数字一样:当要强调符号时,数字前面可以带上正号或负号;然而,当很明显确定数字为正数时,就不需要加上正号了。有符号数字以补码形式存储。

每个有符号类型规定的数字范围是 -(2n - 1) ~ 2n - 1 - 1,其中 n 是该定义形式的位长度。因此 i8 可存储数字范围是 -(27) ~ 27 - 1,即 -128 ~ 127。无符号类型可以存储的数字范围是 0 ~ 2n - 1,所以 u8 能够存储的数字为 0 ~ 28 - 1,即 0 ~ 255。

整数溢出

例如:u8的范围是0-255,如果你把一个u8变量的值设为256,那么

  1. 在调试模式下:rust会检查整数溢出,如果发生溢出,程序在运行时会报错。
  2. 在发布模式下(–release):rust不会检查可能导致报错的整数溢出。如果溢出发生,会执行“环绕”操作,就像一个钟表一样转圈,将256变成0,257变成1…以此类推,且不会报错。

(二)浮点类型

浮点类型数字 是带有小数点的数字,在 Rust 中浮点类型数字也有两种基本类型: f32 和 f64,分别为 32 位和 64 位大小。默认浮点类型是 f64,在现代的 CPU 中它的速度与 f32 几乎相同,但精度更高。

下面是一个演示浮点数的示例:

fn main() {
    let x = 2.0; // f64
    let y: f32 = 3.0; // f32
}

浮点数根据 IEEE-754 标准实现。f32 类型是单精度浮点型,f64 为双精度。

(三)字符类型

Rust 的 char 类型是语言中最原生的字母类型。下面是一些声明 char 值的例子:

fn main() {
    let c = 'z';
    let z: char = 'ℤ';
    let g = '国';
    let heart_eyed_cat = '😻';
}

Rust 的字符不仅仅是 ASCII,所有的 Unicode 值都可以作为 Rust 字符,包括单个的中文、日文、韩文、emoji 表情符号等等,都是合法的字符类型。Unicode 值的范围从 U+0000 ~ U+D7FF 和 U+E000 ~ U+10FFFF。

由于 Unicode 都是 4 个字节编码,因此字符类型也是占用 4 个字节。

(四)布尔类型

正如其他大部分编程语言一样,Rust 中的布尔类型有两个可能的值:true 和 false。Rust 中的布尔类型使用 bool 表示,且布尔值占用内存的大小为 1 个字节:

fn main() {
    let t = true;
    let f: bool = false; // 使用类型标注,显式指定f的类型
    if f {
        println!("这是段毫无意义的代码");
    }
}

使用布尔类型的场景主要在于流程控制,例如上述代码的中的 if 就是其中之一。

二、复合类型

复合类型可以将多个值放在一个类型里。

Rust提供了两种基础的复合类型:元组、数组。

(一)元组

元组是一个将多个其他类型的值组合进一个复合类型的主要方式。元组长度固定,一旦声明结束,其长度不会增大或缩小。

我们在小括号里,将值用逗号分开来表示创建一个元组。元组中的每一个位置都有一个类型,而且这些不同值的类型也不必是相同的。如下表示创建一个元组:

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
}

//let 元组变量名:(类型1, 类型2, 类型3) = (值1, 值2, 值3);

变量 tup 被绑定了一个元组值 (500, 6.4, 1),该元组的类型是 (i32, f64, u8)。

所以元组就是用括号将多个类型组合到一起。

可以使用模式匹配或者 . 操作符来获取元组中的值。

用模式匹配解构元组
fn main() {
    let tup = (500, 6.4, 1);

    let (x, y, z) = tup;

    println!("The value of y is: {}", y);
}

上述代码首先创建一个元组,然后将其绑定到 tup 上,接着使用 let (x, y, z) = tup; 来完成一次模式匹配,因为元组是 (n1, n2, n3) 形式的,因此我们用一模一样的 (x, y, z) 形式来进行匹配,元组中对应的值会绑定到变量 x, y, z上。这就是解构:用同样的形式把一个复杂对象中的值匹配出来。

用.来访问元组

模式匹配可以让我们一次性把元组中的值全部或者部分获取出来,如果只想要访问某个特定元素,那模式匹配就略显繁琐,对此,Rust 提供了 . 的访问方式:

fn main() {
    let x: (i32, f64, u8) = (500, 6.4, 1);

    let five_hundred = x.0;

    let six_point_four = x.1;

    let one = x.2;
}

和其它语言的数组、字符串一样,元组的索引从 0 开始。

(二)数组

另一个包含多个值的方式是 数组array)。

与元组不同,数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,Rust 中的数组长度是固定的。

在 Rust 中,数组是这样定义的:

fn main() {
    let a = [1, 2, 3, 4, 5];
}

数组语法跟 JavaScript 很像,也跟大多数编程语言很像。由于它的元素类型大小固定,且长度也是固定,因此数组 array 是存储在栈上,性能也会非常优秀。

但是数组却没有动态数组(Vector)灵活。vector 类型是标准库提供的一个 允许 增长和缩小长度的类似数组的集合类型,我们称为“动态数组”。动态数组 Vector 是存储在堆上,因此长度可以动态改变。当你不确定是使用数组还是动态数组时,那就应该使用后者。

举个例子,在需要知道一年中各个月份名称的程序中,你很可能希望使用的是数组而不是动态数组。因为月份是固定的,它总是只包含 12 个元素:

let months = ["January", "February", "March", "April", "May", "June", "July",
              "August", "September", "October", "November", "December"];

在一些时候,还需要为数组声明类型,如下所示:

let a: [i32; 5] = [1, 2, 3, 4, 5];

这里,数组类型是通过方括号语法声明,i32 是元素类型,分号后面的数字 5 是数组长度,数组类型也从侧面说明了数组的元素类型要统一,长度要固定

还可以使用下面的语法初始化一个某个值重复出现 N 次的数组

let a = [3; 5];
// let a = [3,3,3,3,3]

a 数组包含 5 个元素,这些元素的初始化值为 3,不难发现,这种语法跟数组类型的声明语法其实是保持一致的:[3; 5] 和 [类型; 长度]。在元素重复的场景,这种写法要简单的多。

访问数组元素

因为数组是Stack上分配的单个块内存,元素的存放顺序是连续的,因此可以通过索引的方式来访问存放其中的元素:

fn main() {
    let a = [9, 8, 7, 6, 5];

    let first = a[0]; // 获取a数组第一个元素
    let second = a[1]; // 获取第二个元素
}

与许多语言类似,数组的索引下标是从 0 开始的。此处,first 获取到的值是 9,second 是 8。

如果访问的索引超出了数组的范围,那么就会引发越界访问错误。

如果是显性的越界访问错误,那么rust就会在编译时及时阻止我们:

fn main() {
    let a = [9, 8, 7, 6, 5];
    let s = a[5];
    println!("{}",s);
}
// let s = a[5]; index out of bounds: the length is 5 but the index is 5

如果是隐性的越界访问错误,那么rust编译会通过,但运行时程序会崩溃报错:

use std::io;

fn main() {
    let a = [1, 2, 3, 4, 5];

    println!("Please enter an array index.");

    let mut index = String::new();
    // 读取控制台的输出
    io::stdin()
        .read_line(&mut index)
        .expect("Failed to read line");

    //读取到的字符串转为无符号整型数字
    let index: usize = index
        .trim()
        .parse()
        .expect("Index entered was not a number");

    //索引访问
    let element = a[index];

    println!(
        "The value of the element at index {} is: {}",
        index, element
    );
}

使用 cargo run 来运行代码,因为数组只有 5 个元素,如果我们试图输入 5 去访问第 6 个元素,则会访问到不存在的数组元素,最终程序会崩溃退出。

这就是数组访问越界,访问了数组中不存在的元素,导致 Rust 运行时错误。程序因此退出并显示错误消息,未执行最后的 println! 语句。

举报

相关推荐

0 条评论