Rust 学习(开篇)

发表于:2022-10-05 20:35:32·阅读:98

此刻,就从 Rust 开始挑战一下自己,挑战一下那些一直学不下去的东西。

本想写一些学习笔记,但是发现已经有这么好的教程了,https://github.com/sunface/rust-course 。所以下面的内容,大家也可以不用看了,哈哈哈,就当是我的学习开篇。

环境搭建

安装 Rust

无论使用何种系统, 均可以根据 Rust 官方网站提供的 rustup-init 工具完成 Rust 的安装. rustup-init 下载地址:

https://www.rust-lang.org/zh-CN/tools/install.

网站会自动识别你的操作系统并给出提示, 遵循网站提示一步一步执行即可.

当安装完成后, 可使用以下命令进行测试, 如果正确输出版本号则表明安装已经成功.

$ rustc --version

提示: 如果你使用的是 Linux 系统, 那么在 rustup-init 运行完成后它会在命令行中提示你将软件安装目录加入 PATH 环境变量中. 对于 Windows 系统来说不需要这一步.

开发工具

使用 vscode, 并配置 rust-analyzer 插件。

  1. 安装 vscode
  2. 在 vscode 的插件市场中, 安装 rust-analyzer 插件,rust-analyzer 包含代码提示, 代码检查, 自动补全等多种功能.

Hello World

Cargo 是 Rust 默认的项目管理工具, 它几乎会贯穿你的整个 Rust 开发周期, 包括项目的创建, 引入第三方库, 编译, 测试和运行等. 使用如下命令可以创建一个新的 Rust 项目:

$ cargo new hello

Cargo 默认会携带 --bin 参数, 这意味着该项目是一个二进制程序. 如果要创建一个库, 我们需要传递 --lib.

生成的项目目录结构如下:

.
├── Cargo.toml
└── src
    └── main.rs
  • Cargo.toml 是项目的描述文件, 它里面保存了项目的依赖库, 项目的名称, 版本号等信息
  • src 是源码目录
  • src/main.rs 是项目的入口点

使用如下命令可以编译并运行项目:

$ cargo run

Hello World!

Cargo

Cargo 里面有许多有用的命令, 一些常用的命令包括:

  • cargo new 生成新的项目模板
  • cargo build 构建项目, 生成可执行文件或依赖
  • cargo run 构建并运行项目
  • cargo test 运行测试用例
  • cargo check 检查项目代码, 由于 Rust 编译较慢, 因此在开发中常用 check 代替 build 命令
  • cargo doc 生成项目文档
  • cargo publish 将库发布到 crates.io

除了以上 cargo 自带的命令外, cargo 还支持安装额外的扩展命令, 例如格式化工具. rustfmt 是一个可以自定义风格的 rust 代码格式化工具, 使用如下命令安装它:

$ rustup component add rustfmt

在项目根目录输入以下命令, 会自动格式化项目内的全部 Rust 源文件.

$ cargo fmt

数据类型

Rust 是静态强类型语言.

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

变量和可变性

创建和使用变量

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

fn main() {
    let x = 5;
    println!("The value of x is: {}", x);
}

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

可变性

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

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 来使它们可变.

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, 常量始终是不可变的
  • 必须显示标注常量的类型
  • 常量可以在任何作用域中声明, 包括全局作用域
  • 常量只能设置为常量表达式, 而不能设置为函数调用的结果或只能在运行时计算的任何其他值.
const A_CONST: i32 = 1;

常量和不可变变量的概念有时候可能比较容易搞混,最大的区别在于:常量是在编译期间进行求值的,而不可变变量实在运行期间进行求值的。如下面这样:

fn main() {
    // 这是正确的
    const A_COUNT: i32 = 1 + 2 + 3;

    fn get_number() -> i32 {
        42
    }
    // 这是错误的,函数是在运行期间执行的,所以不能赋给常量
    const B_COUNT: i32 = get_number();

    // 这是正确的,不可变变量是在运行期间求值的
    let b = get_number();
}

隐藏(Shadowing)

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

fn main() {
    let x = 5;

    let x = x + 1;

    let x = x * 2;

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

基本类型

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.
  • isizeusize 根据系统的不同而有不同的长度.

浮点数

Rust 有两种浮点数类型, 为 f32 和 f64, 后者精度更高.

对于未明确标注类型的小数, Rust 默认采用 f64.

fn main() {
    let x = 2.0; // f64

    let y: f32 = 3.0; // f32
}

布尔值

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

fn main() {
    let t = true;

    let f: bool = false;
}

字符

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

fn main() {
    let c = 'z';
    let z = 'ℤ';
    let heart_eyed_cat = '😻';
}

复合类型

元组

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

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

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 存在越界检查.

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 成员表示切片长度.

fn main() {
    let arr: [i32; 5] = [1, 2, 3, 4, 5];
    let slice = &arr[0..3]; // 取前 3 个元素
    println!("slice[0]={}, len={}", slice[0], slice.len());
}

字符串

直接参见:https://course.rs/basic/compound-type/string-slice.html

结构体

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

  • 元组结构
  • 经典的 C 结构
  • 无字段的单元结构(通常在泛型里使用较多)

结构体使用驼峰命名.

// 元组结构
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);
}
评论
文明评论,理性发言
⌘ + Enter
全部评论
暂无评论数据