前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Rust基本数据类型

Rust基本数据类型

作者头像
端碗吹水
发布2022-06-01 09:07:39
8490
发布2022-06-01 09:07:39
举报

[TOC]


类型系统概述

什么是类型?类型是对二进制数据的一种约束行为。类型比起直接使用二进制数据,有许多优势:

  • 减少开发者心智负担
  • 安全
  • 容易优化

常见的类型分类:

  • 静态类型:在编译期对类型进行检查
  • 动态类型:在运行期对类型进行检查
  • 强类型:不允许隐式类型转换
  • 弱类型:允许进行隐式类型转换

C 语言由于允许隐式类型转换因此是静态弱类型语言,许多人易将 C 语言误认为静态强类型,需要特别注意:

代码语言:javascript
复制
int main() {
    long a = 10;
    return a;
}
  • Rust 是静态强类型语言

变量和可变性

创建和使用变量

在 Rust 代码中,可以使用 let 关键字将值绑定到变量:

代码语言:javascript
复制
fn main() {
    let x = 5;
    println!("The value of x is: {}", x);
}

println 是一个宏,它是最常用的将数据打印在屏幕上的方法。目前,我们可以简单地将它视为一个拥有可变参数数量的函数,在后面的章节中我们会对宏进行详细的讨论。

可变性

在 Rust 中,变量默认是不可变的,一旦一个值绑定到一个名称,就不能更改该值:

代码语言:javascript
复制
fn main() {
    let x = 5;
    println!("The value of x is: {}", x);
    x = 6;  // cannot assign twice to immutable variable `x`
    println!("The value of x is: {}", x);
}

但有时候允许变量可变是非常有用的。通过在变量名前面添加 mut 来使它们可变:

代码语言:javascript
复制
fn main() {
    let mut x = 5;
    println!("The value of x is: {}", x);
    x = 6;
    println!("The value of x is: {}", x);
}

常量和变量

不可变变量容易让你联想到另一个概念:常量。在 Rust 中,常量使用 const 定义,而变量使用 let 定义:

  • 不允许对常量使用修饰词 mut,常量始终是不可变的
  • 必须显示标注常量的类型
  • 常量可以在任何作用域中声明,包括全局作用域
  • 常量只能设置为常量表达式,而不能设置为函数调用的结果或只能在运行时计算的任何其他值
代码语言:javascript
复制
const A_CONST: i32 = 1;

隐藏(Shadowing)

可以声明一个与前一个变量同名的新变量,并且新变量会隐藏前一个变量,这种操作被称为隐藏(Shadowing)。

代码语言:javascript
复制
fn main() {
    let x = 5;

    let x = x + 1;

    let x = x * 2;

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

基础数据类型

Rust 是一门静态编程语言,所有变量的类型必须在编译期就被明确固定。

整数

Rust 中有 12 种不同的整数类型:

长度

有符号

无符号

8-bit

i8

u8

16-bit

i16

u16

32-bit

i32

u32

64-bit

i64

u64

128-bit

i128

u128

arch

isize

usize

  • 对于未明确标注类型的整数,Rust 默认采用 i32.
  • isize 和 usize 根据系统的不同而有不同的长度.

浮点数

Rust 有两种浮点数类型,为 f32f64,后者精度更高。对于未明确标注类型的小数,Rust 默认采用 f64.

代码语言:javascript
复制
fn main() {
    let x = 2.0; // f64

    let y: f32 = 3.0; // f32
}

布尔值

与大多数其他编程语言一样,Rust 中的布尔类型有两个可能的值:truefalse。布尔值的大小是一个字节。

代码语言:javascript
复制
fn main() {
    let t = true;

    let f: bool = false;
}

字符

Rust 支持单个字符,字符使用单引号包装。

代码语言:javascript
复制
fn main() {
    let c = 'z';
    let z = 'ℤ';
    let heart_eyed_cat = '😻';
}

整数溢出

在电脑领域里所发生的溢出条件是,运行单项数值计算时,当计算产生出来的结果是非常大的,大于寄存器或存储器所能存储或表示的能力限制就会发生溢出。

在不同的编程语言中,对待溢出通常有以下几种不同的做法:

  • 崩溃:当溢出被侦测到时,程序立即退出运行
  • 忽略:这是最普遍的作法,忽略任何算数溢出

对于溢出的处理方法,Rust 在 debug 与 release 模式下是不同的。在 debug 模式下编译时,Rust 会检查整数溢出,如果发生这种行为,会导致程序在运行时终止并报出运行时错误。而如果在 release 模式下编译时,Rust 不会对整数溢出进行检查。

要显式处理溢出,可以使用标准库提供的一些 .overflowing_* 方法:

代码语言:javascript
复制
fn main() {
    let a: u32 = 4294967295;
    let b: u32 = 1;

    let (r, is_overflow) = a.overflowing_add(b);
    println!("r={} is_overflow={}", r, is_overflow);
}

元组

元组是将多个具有各种类型的值组合成一个复合类型的通用方法。元组有固定的长度:一旦声明,它们的大小就不能增长或收缩。

我们通过在括号内写一个逗号分隔的值列表来创建一个元组。元组中的每个位置都有一个类型,元组中不同值的类型不必相同。

代码语言:javascript
复制
fn main() {
    let a: i32 = 10;
    let b: char = 'A';

    // 创建一个元组
    let mytuple: (i32, char) = (a, b);

    // 从元组中读取一个值
    println!(".0={:?}", mytuple.0);
    println!(".1={:?}", mytuple.1);

    // 解封装
    let (c, d) = mytuple;
    println!("c={} d={}", c, d);
}

数组

另一种拥有多个数据集合的方法是使用数组。与元组不同,数组中的每个元素都必须具有相同的类型。Rust 中的数组不同于其他一些语言中的数组,Rust 中的数组具有固定长度。

数组下标以 0 开始,同时 Rust 存在越界检查:

代码语言:javascript
复制
fn main() {
    // 创建数组, [i32; 3] 是数组的类型提示, 表示元素的类型是 i32, 共有 3 个元素
    let myarray: [i32; 3] = [1, 2, 3];

    // 根据索引获取一个值, 数组下标从 0 开始
    println!("{:?}", myarray[1]);

    // 索引不能越界
    println!("{:?}", myarray[3]);

    // 如果数组的每个元素都有相同的值, 我们还可以简化数组的初始化
    let myarray: [i32; 3] = [0; 3];
    println!("{:?}", myarray[1]);
}

切片类型

切片类型是对一个数组(包括固定大小数组和动态数组)的引用片段,有利于安全有效地访问数组的一部分,而不需要拷贝数组或数组中的内容。切片在编译的时候其长度是未知的,在底层实现上,一个切片保存着两个 uszie 成员,第一个 usize 成员指向切片起始位置的指针,第二个 usize 成员表示切片长度:

代码语言:javascript
复制
fn main() {
    let arr: [i32; 5] = [1, 2, 3, 4, 5];
    let slice = &arr[0..3]; // 取前 3 个元素,.. 是 Rust Range 语法,& 是引用符号
    println!("slice[0]={}, len={}", slice[0], slice.len());
}

结构体

结构体是多种不同数据类型的组合。它与元组类似,但区别在于我们可以为每个成员命名。可以使用 struct 关键字创建三种类型的结构:

  • 元组结构
  • 经典的 C 结构
  • 无字段的单元结构

结构体使用驼峰命名:

代码语言:javascript
复制
// 元组结构
struct Pair(i32, f32);

// 经典的 C 结构
struct Person {
    name: String,
    age: u8,
}

// 无字段的单元结构, 在泛型中较为常用
struct Unit;

fn main() {
    // 结构体的实例化
    let pair = Pair(10, 4.2);
    let person = Persion {
        name: String::from("jack"),
        age: 21,
    };
    let unit = Unit;

    // 从结构体中获取成员
    println!("{}", pari.0);
    println!("{}", persion.name);
}

枚举

enum 关键字可创建枚举类型。枚举类型包含了取值的全部可能的情况。在 Rust 中,有多种不同形式的枚举写法。

无参数的枚举

代码语言:javascript
复制
enum Planet {
    Mars,
    Earth,
}

上面的代码定义了枚举 Planet,包含了两个值 Mars 和 Earth。

带枚举值的枚举

代码语言:javascript
复制
enum Color {
    Red = OxffOOOO,
    Green = OxOOffOO,
    Blue = OxOOOOff,
}

带参数的枚举

Rust 还支持携带类型参数的枚举:

代码语言:javascript
复制
enum IpAddr {
    IPv4(u8, u8, u8, u8),
    IPv6(u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8),
}

模式匹配

枚举通常与 match 模式匹配一起使用:

代码语言:javascript
复制
enum IpAddr {
    IPv4(u8, u8, u8, u8),
    IPv6(u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8),
}

fn main() {
    let localhost: IpAddr = IpAddr::IPv4(127, 0, 0, 1);
    match localhost {
        IpAddr::IPv4(a, b, c, d) => {
            println!("{} {} {} {}", a, b, c, d);
        }
        _ => {} // 任何非 IPv4 类型走这条分支
    }
}

各种注释类型

与许多现代语言一样,Rust 也支持丰富的注释种类,我们可以通过注释来了解一段代码干了什么工作,甚至可以直接通过注释生成文档。

普通的注释

代码语言:javascript
复制
// 使用 // 注释单行

/*
也可以使用 /* */ 注释多行, 这一点与 C 语言是一样的
*/

文档注释

文档注释是一种 Markdown 格式的注释,用于对文档中的代码生成文档。可以使用 cargo doc 工具生成 HTML 文挡。

代码语言:javascript
复制
//! 这是模块级别的文档注释, 一般用于模块文件的头部

/// 这是文档注释, 一般用于函数或结构体的说明, 置于说明对象的上方.
struct Person;

例子

下面的代码演示了斐波那契函数及其注释,使用 cargo doc 构建 HTML 文档:

代码语言:javascript
复制
//! A main project provides fibonacci function

/// In mathematics, the Fibonacci numbers, commonly denoted Fn form a sequence, called the Fibonacci sequence, such that
/// each number is the sum of the two preceding ones, starting from 0 and 1. That is
/// ```
/// F(0) = 0
/// F(1) = 1
/// F(n) = F(n − 1) + F(n − 2)
/// ```
fn fibo(n: u32) -> u32 {
    if n== 0 || n == 1 {
        n
    } else {
        fibo(n - 1) + fibo(n - 2)
    }
}

fn main() {
    // Calculate fibo(10)
    println!("fibo(10) = {}", fibo(10));
    /*
    The result should be 55
    */
}
image.png
image.png

println函数

println! 用于将数据打印到标准输出,且在数据末尾自动带上换行符。在所有平台上,换行符都是换行符(没有额外的回车符)。

使用 println! 用于程序的正常输出,使用 eprintln! 打印错误或者进度条。前者数据被写入 stdout,后者则是 stderrprintln! 宏常用的格式化语法如下所示:

代码语言:javascript
复制
fn main() {
    // `{}` 会被变量内容替换, 这是最常见的一种用法
    println!("{}", 42);

    // 可以使用额外的位置参数.
    println!("{0}{1}{0}", 4, 2);

    // 使用命名参数.
    println!("name={name} age={age}", name="jack", age=6);

    // 可以在 `:` 后面指定特殊的格式.
    println!("{} of {:b} people know binary, the other half don't", 1, 2);

    // 可以按指定宽度来右对齐文本.
    println!("{number:>width$}", number=1, width=6);

    // 在数字左边补 0.下面语句输出 "000001".
    println!("{number:>0width$}", number=1, width=6);

    // println! 会检查使用到的参数数量是否正确.
    println!("My name is {0}, {1} {0}", "Bond");
    // 编译将会报错, 请补上漏掉的参数:"James"
}

在不同类型之间转换

Rust 是一门强类型语言,因此不支持隐式类型转换。Rust 为了实现类型之间的转换提供了几种不同的方法。

as 语法

as 语法是 Rust 最基础的一种类型转换方法,它通常用于整数,浮点数和字符数据之间的类型转换:

代码语言:javascript
复制
fn main() {
    let a: i8 = -10;
    let b = a as u8;
    println!("a={} b={}", a, b);
}

数值转换的语义是:

  • 两个相同大小的整型之间(例如:i32 -> u32)的转换是一个 no-op
  • 从一个大的整型转换为一个小的整型(例如:u32 -> u8)会截断
  • 从一个小的整型转换为一个大的整型(例如:u8 -> u32)会
    • 如果源类型是无符号的会补零(zero-extend)
    • 如果源类型是有符号的会符号(sign-extend)
  • 从一个浮点转换为一个整型会向 0 舍入
  • 从一个整型转换为一个浮点会产生整型的浮点表示,如有必要会舍入(未指定舍入策略)
  • 从 f32 转换为 f64 是完美无缺的
  • 从 f64 转换为 f32 会产生最接近的可能值(未指定舍入策略)

transmute

as 只允许安全的转换,例如会拒绝例如尝试将 4 个字节转换为一个 u32

代码语言:javascript
复制
let a = [0u8, 0u8, 0u8, 0u8];
let b = a as u32; // Four u8s makes a u32.

但是我们知道 u32 在内存中表示为 4 个连续的 u8,因此我们可以使用一种危险的方法:告诉编译器直接以另一种数据类型对待内存中的数据。编译器会无条件信任你,但是,除非你知道自己在干什么,不然并不推荐使用 transmute。要使用 transmute,需要将代码写入 unsafe 块中:

代码语言:javascript
复制
fn main() {
    unsafe {
        let a = [0u8, 1u8, 0u8, 0u8];
        let b = mem::transmute::<[u8; 4], u32>(a);
        println!("{}", b); // 256
        // Or, more concisely:
        let c: u32 = mem::transmute(a);
        println!("{}", c); // 256
    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-05-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 类型系统概述
  • 变量和可变性
    • 创建和使用变量
      • 可变性
        • 常量和变量
          • 隐藏(Shadowing)
          • 基础数据类型
            • 整数
              • 浮点数
                • 布尔值
                  • 字符
                  • 整数溢出
                  • 元组
                  • 数组
                  • 切片类型
                  • 结构体
                  • 枚举
                    • 无参数的枚举
                      • 带枚举值的枚举
                        • 带参数的枚举
                          • 模式匹配
                          • 各种注释类型
                            • 普通的注释
                              • 文档注释
                                • 例子
                                • println函数
                                • 在不同类型之间转换
                                  • as 语法
                                    • transmute
                                    领券
                                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档