菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
132
0

Rust: 基础类型

原创
05/13 14:22
阅读数 84141

前面我们讲解了 Rust 变量的相关内容,其中涉及到了变量类型,今天就来介绍一下基础类型吧。

Rust 包含以下几种基础类型:

  • 布尔类型
  • 字符类型
  • 整数类型
  • 浮点数类型
  • 数组类型
  • 切片类型
  • 元组类型
  • 字符串类型

下面我们就来逐个地进行介绍:

一、布尔类型

布尔类型使用 bool 关键字来表示,它仅有两个值:truefalse,这和大多数编程语言都相同:

fn main() {
    // 自动推断为bool类型
    let is_real = true;
    
    if is_real {
        println!("is real");
    }

    // 显式声明为bool类型
    let is_real: bool = false;

    if !is_real {
        println!("not real");
    }
}

二、字符类型

字符类型使用 char 关键字来表示,代表一个 Unicode 编码的字符,它占用 4 个字节,即 32 位,可涵盖世界范围内的所有语言文字。字符值须使用两个单引号来包裹:

fn main() {
    // 自动推断为char类型
    let a = 'a';

    // 显式声明为char类型
    let b: char = 'b';

    println!("{} {}", a, b);

    // 中文
    let zh = '好';
    
    println!("{}", zh);

    // Unicode特殊字符
    let super_z = 'ℤ';

    // Unicode图案
    let super_cat = '?';

    println!("{} {}", super_z, super_cat);
}

需要注意的是,字符类型要求在声明字面量时,必须为其指定一个具体的字符,若不指定,编译时将会报错:

fn main() {
    // error: empty character literal
    let empty = '';
    
    println!("{}", empty);
}

三、整数类型

在 Rust 中,整数类型又分为 有符号整数无符号整数 两个部分。

1. 有符号整数类型(signed integer)

有符号整数类型包括以下几种:

  • i8,占用 8 位(即 1 个字节),范围在 [-128, 127] 之间,相当于 Java 中的 byte 类型;
  • i16,占用 16 位(即 2 个字节),范围在 [-2^15, 2^15 - 1] 之间,相当于 Java 中的 short 类型;
  • i32,占用 32 位(即 4 个字节),范围在 [-2^31, 2^31 - 1] 之间,相当于 Java 中的 int 类型;
  • i64,占用 64 位(即 8 个字节),范围在 [-2^63, 2^63 - 1] 之间,相当于 Java 中的 long 类型;
  • i128,占用 128 位(即 16 个字节),范围在 [-2^127, 2^127 - 1] 之间;
  • isize,根据平台决定存储位数,在 32 位平台下占用 32 位,在 64 位平台下占用 64 位;

由于有符号整数的最高位,被用来存储当前数值的符号,所以真实值的存储位只有 n - 1 位。

如果我们在声明变量时,没有指定类型,编译器会默认使用 i32 类型,我们也可以显示地声明类型:

fn main() {
    // 自动推断为i32类型
    let a = 1;
    
    // 显式声明为i32类型
    let b: i32 = 3;
    
    // 根据平台来决定存储位数
    let c: isize = 5;
    
    println!("{} {} {}", a, b, c);
}

2. 无符号整数类型(unsigned integer)

无符号整数类型包括以下几种:

  • u8,占用 8 位(即 1 个字节),范围在 [0, 255] 之间;
  • u16,占用 16 位(即 2 个字节),范围在 [0, 2^16] 之间;
  • u32,占用 32 位(即 4 个字节),范围在 [0, 2^32] 之间;
  • u64,占用 64 位(即 8 个字节),范围在 [0, 2^64] 之间;
  • u128,占用 128 位(即 16 个字节),范围在 [0, 2^128] 之间;
  • usize,根据平台决定存储位数,在 32 位平台下占用 32 位,在 64 位平台下占用 64 位;

由于无符号整数的所有位数,都是用来存储真实值的,所以它只能表示正整数,也正是因为这个,它的正数可表示范围,要比有符号整数大一些。

在声明无符号整数时,必须显式地指明类型信息,原因很简单,如果没有显示声明,就会被编译器推断为 i32 类型:

fn main() {
    let a: u32 = 1;
    let b: u64 = 3;
    let c: usize = 5;
    
    println!("{} {} {}", a, b, c);
}

以上就是整数类型,接下来我们来介绍浮点数类型。

四、浮点数类型

Rust 中的浮点数遵循 IEEE 754 标准,包含 单精度浮点数双精度浮点数 两种类型:

  • f32,单精度浮点数,占用 32 位(即 4 个字节),相当于 Java 中的 float 类型;
  • f64,双精度浮点数,占用 64 位(即 8 个字节),相当于 Java 中的 double 类型;

在声明浮点型变量时,如果不指定类型,编译器会默认使用 f64 类型,所以如果我们想定义一个 f32 类型的变量,则需要显示声明类型信息:

fn main() {
    // 默认为f64类型
    let a = 1.0;

    // 显式声明为f32类型
    let b: f32 = 3.0;

    println!("{:?} {:?}", a, b);
}

五、数组类型

数组在 Rust 中是一种 元素类型统一容量大小固定 的数据结构,一旦声明,数组的长度和元素的类型都不可再更改。

下面是几种常见的数组定义方式:

fn main() {
    // 编译器推断为[i32; 5]类型
    let a = [1, 2, 3, 4, 5];

    // 显式声明类型信息
    let b: [i32; 5] = [1, 2, 3, 4, 5];

    println!("{:?} {:?}", a, b);

    // 包含5个元素 每个元素初始化值为1
    let c = [1; 5];

    // 等价于下面这种方式
    let d = [1, 1, 1, 1, 1];

    println!("{:?} {:?}", c, d);
}

从上面代码可以看出,数组的类型由两部分组成:[elem_type; length],即元素类型和数组长度。

在表示数组元素时,可以直接以字面量形式列出所有元素,也可以指定元素初始值和数组长度,即 [init_value; length] 这种形式。

如果我们想更改数组内的元素值,则必须为数组加上 mut 关键字修饰:

fn main() {
    // 加上mut才允许更改数组元素
    let mut a = [1, 2, 3, 4, 5];

    // 将第一个元素改为6
    a[0] = 6;
    
    // 打印结果[6, 2, 3, 4, 5]
    println!("{:?}", a);
}

如果两个数组的 元素类型长度 都相同,则可以相互赋值:

fn main() {
    let mut a = [1, 2, 3, 4, 5];
    
    // 打印结果[1, 2, 3, 4, 5]
    println!("{:?}", a);
    
    let b = [3; 5];
    
    // 成功赋值
    a = b;
    
    // 打印结果[3, 3, 3, 3, 3]
    println!("{:?}", a);
}

反之,如果类型和长度有任何一项不相同,则两个数组是不同的类型,赋值将会导致编译报错。

最后,数组提供了 len()iter() 两个方法,我们可以利用它们,来迭代数组内部的元素,下面对比了两种迭代方式的不同:

fn main() {
    let ary = [5, 6, 7];
    
    // 通过索引迭代
    for i in 0..ary.len() {
        println!("{}", ary[i]);
    }
    
    // 直接迭代元素
    for elem in ary.iter() {
        println!("{}", elem);
    }
}

六、切片

切片是在源数据集基础之上创建的的引用(或称为视图),它包含了源数据集在指定范围内的子区间数据。

下面我们使用数组作为源数据集,演示一下如何创建切片:

fn main() {
    let ary = [5, 6, 7];
    
    // 推断为&[i32]类型
    let a = &ary[..];
    
    // 显式声明为&[i32]类型
    let b: &[i32] = &ary[..];
    
    println!("{:?} {:?}", a, b);
}

在上面的代码中,我们可以看得出来,切片的类型是 &[T],其中 T 表示数据集元素的类型。

另外,我们使用 &ary[..] 这样的语法获取一个切片,它表示当前切片是基于 ary 这个数据集创建的,子区间的范围是 0..ary.len(),其中对于 & 符号,我们可以理解为一个引用。

切片的区间范围有很多种表示方式,我们来看一下:

fn main() {
    let ary = [6, 7, 8, 9, 10];
    
    // 所有元素 [6, 7, 8, 9, 10]
    let a = &ary[..];
    
    // 索引1处开始 [7, 8, 9, 10]
    let b = &ary[1..];
    
    // 索引3处停止 [6, 7, 8]
    let c = &ary[..3];
    
    println!("{:?} {:?} {:?}", a, b, c);
    
    // 范围[1, 3) 结果[7, 8]
    let d = &ary[1..3];
    
    // 范围[1, 3] 结果[7, 8, 9]
    let e = &ary[1..=3];
    
    println!("{:?} {:?}", d, e);
}

切片的迭代和数组很相似,不同的是,切片也可以直接进行元素的迭代:

fn main() {
    let ary = [5, 6, 7];
    
    let slice = &ary[..];
    
    // 与数组类似 迭代索引
    for i in 0..slice.len() {
        println!("{}", i);
    }
    
    // 与数组类似 迭代元素
    for elem in slice.iter() {
        println!("{}", elem);
    }
    
    // 可直接迭代元素
    for elem in slice {
        println!("{}", elem);
    }
}

七、元组类型

元组是一组固定容量的有序列表,和数组不同,元组对元素的类型不做要求,所以我们能在元组中存放各种类型的数据:

fn main() {
    // 声明一个元组
    let a = (500, 6.4, 1);
    
    // 显式声明类型信息
    let b: (i32, f64, u8) = (500, 6.4, 1);
    
    println!("{:?} {:?}", a, b);
}

元组可以通过解构来获取到内部的元素:

fn main() {
    let a = (500, 6.4, 1);
    
    // 解构
    let (x, y, z) = a;
    
    println!("{:?} {:?} {:?}", x, y, z);
}

另外,元组还提供了以索引的方式访问内部元素:

fn main() {
    let a = (500, 6.4, 1);
    
    // 通过索引的方式
    let x = a.0;
    let y = a.1;
    let z = a.2;
    
    println!("{:?} {:?} {:?}", x, y, z);
}

和数组类似,通过索引我们也可以更改元组在指定索引处的值:

fn main() {
    // 加上mut修饰
    let mut a = (500, 6.4, 1);
    
    // 更改第一个元素的值
    a.0 = 600;
    
    // 获取最新值
    let x = a.0;
    let y = a.1;
    let z = a.2;
    
    // 打印结果600 6.4 1
    println!("{:?} {:?} {:?}", x, y, z);
}

和数组类似,相同类型的元组之间也是可以相互赋值的:

fn main() {
    // 加上mut修饰
    let mut a = (500, 6.4, 1);
    
    // 打印(500, 6.4, 1)
    println!("{:?}", a);

    let b = (600, 6.5, 2);
    
    // 成功赋值
    a = b;
    
    // 打印(600, 6.5, 2)
    println!("{:?}", a);
}

八、字符串类型

当我们谈到 Rust 的字符串时,会涉及到以下两个概念:

  • 原生字符串:类型可表示为 &str,容量大小固定,一旦声明就不可更改。
  • 标准库字符串:即 std::string::String 类型,容量可变,内容可更改,并且能与原生字符串相互转化。

下面我们来演示一下,如何声明这两种字符串:

fn main() {
    // 推断为&str类型
    let a = "hello";
    
    // 显式声明为&str类型
    let b: &str = "hello";
    
    // 转为标准库的String类型
    let c: String = b.to_string();
    
    // 通过&str创建String类型变量
    let mut d: String = String::from(b);
    
    // 有mut修饰 才可进行追加
    d.push_str(" rust");
    
    // 追加一个字符
    d.push('!');
    
    println!("{} {} {} {}", a, b, c, d);
}

关于字符串,这里不做过多讲解,接下来我们会用专门的篇幅进行介绍。

以上就是 Rust 基础类型的相关内容了,基础类型是一门编程语言的重要组成部分,希望大家都可以熟练地掌握这些内容。

发表评论

0/200
132 点赞
0 评论
收藏