2021-07-13

元组

是不同类型值的集合。元组是使用括号构造(),且每个元组本身是具有式签名的值 (T1, T2, …),其中T1,T2是所述类型的成员。函数可以使用元组返回多个值,因为元组可以保存任意数量的值。

数组

是T存储在连续内存中的相同类型对象的集合。数组是使用方括号创建的[],它们的长度(在编译时已知)是它们类型签名的一部分[T; length]。

切片类似于数组,但它们的长度在编译时是未知的。相反,切片是一个双字对象,第一个字是指向数据的指针,第二个字是切片的长度。字长与 usize 相同,由处理器架构决定,例如 x86-64 上的 64 位。切片可用于借用数组的一部分,并具有类型签名 &[T]。

结构

枚举
常数

变量绑定

可变性

默认情况下,变量绑定是不可变的,但这可以使用mut修饰符覆盖。

范围和阴影

变量绑定有一个作用域,并且被限制在一个块中。块是用大括号括起来的一组语句{}。

先申报

可以先声明变量绑定,然后再初始化它们。但是,这种形式很少使用,因为它可能导致使用未初始化的变量。

冷冻

当数据以不变的名称绑定在一起时,它也会冻结。在不可变绑定超出范围之前,无法修改冻结的数据:

fn main() {
    let mut _mutable_integer = 7i32;

    {
        // Shadowing by immutable `_mutable_integer`
        let _mutable_integer = _mutable_integer;

        // Error! `_mutable_integer` is frozen in this scope
        _mutable_integer = 50;
        // FIXME ^ Comment out this line

        // `_mutable_integer` goes out of scope
    }

    // Ok! `_mutable_integer` is not frozen in this scope
    _mutable_integer = 3;
}

类型

铸件

Rust 不提供原始类型之间的隐式类型转换(强制)。但是,可以使用as关键字执行显式类型转换(强制转换)。

整数类型之间的转换规则通常遵循 C 约定,除非 C 具有未定义的行为。整型之间的所有类型转换的行为在 Rust 中都有很好的定义。

文字

数字文字可以通过添加类型作为后缀来进行类型注释。例如,要指定文字42应具有 type i32,请写入42i32。

无后缀数字文字的类型取决于它们的使用方式。如果不存在约束,编译器将i32用于整数和f64浮点数。

fn main() {
    // Suffixed literals, their types are known at initialization
    let x = 1u8;
    let y = 2u32;
    let z = 3f32;

    // Unsuffixed literals, their types depend on how they are used
    let i = 1;
    let f = 1.0;

    // `size_of_val` returns the size of a variable in bytes
    println!("size of `x` in bytes: {}", std::mem::size_of_val(&x));
    println!("size of `y` in bytes: {}", std::mem::size_of_val(&y));
    println!("size of `z` in bytes: {}", std::mem::size_of_val(&z));
    println!("size of `i` in bytes: {}", std::mem::size_of_val(&i));
    println!("size of `f` in bytes: {}", std::mem::size_of_val(&f));
}

前面代码中用到的一些概念还没有解释,这里给不耐烦的读者简单解释一下:

std::mem::size_of_val是一个函数,但以其完整路径调用。代码可以分成称为模块的逻辑单元。在这种情况下, size_of_val函数定义在mem模块中,mem模块定义在std crate 中。有关更多详细信息,请参阅 模块和板条箱。

推理

类型推理引擎非常聪明。它不仅仅是在初始化期间查看值表达式的类型。它还查看之后如何使用变量来推断其类型。这是类型推断的高级示例:

fn main() {
    // Because of the annotation, the compiler knows that `elem` has type u8.
    let elem = 5u8;

    // Create an empty vector (a growable array).
    let mut vec = Vec::new();
    // At this point the compiler doesn't know the exact type of `vec`, it
    // just knows that it's a vector of something (`Vec<_>`).

    // Insert `elem` in the vector.
    vec.push(elem);
    // Aha! Now the compiler knows that `vec` is a vector of `u8`s (`Vec`)
    // TODO ^ Try commenting out the `vec.push(elem)` line

    println!("{:?}", vec);
}

不需要变量的类型注释,编译器很高兴,程序员也很高兴!

别名

该type语句可用于为现有类型赋予新名称。类型必须有UpperCamelCase名称,否则编译器会发出警告。唯一例外的规则是基本类型:usize,f32,等。

转换

from 和 into

tryfrom 和 tryinto

往返字符串

控制流程

if_else

用if-分支else类似于其他语言。与它们中的许多不同,布尔条件不需要用括号括起来,每个条件后跟一个块。if-else条件是表达式,并且所有分支必须返回相同的类型。

loop

Rust 提供了一个loop关键字来表示无限循环。

该break语句可用于随时退出循环,而该 continue语句可用于跳过剩余的迭代并开始新的迭代。

while

for loops

该for in构造可用于遍历Iterator. 创建迭代器的最简单方法之一是使用范围表示法a…b。这会 以 1 的步长产生从a(包含)到b(不包含)的值。或者,a…=b可用于包含两端的范围.

fn main() {
    // `n` will take the values: 1, 2, ..., 100 in each iteration
    for n in 1..101 {
        if n % 15 == 0 {
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
    }
}
 fn main() {
    // `n` will take the values: 1, 2, ..., 100 in each iteration
    for n in 1..=100 {
        if n % 15 == 0 {
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
    }
}

for和迭代器

该for in构造能够以Iterator多种方式与 交互。正如Iterator trait部分所讨论的,默认情况下,for 循环会将into_iter函数应用于集合。但是,这并不是将集合转换为迭代器的唯一方法。

into_iter,iter并且iter_mut都通过提供对其中数据的不同视图,以不同的方式处理集合到迭代器的转换。

iter- 这通过每次迭代借用集合的每个元素。从而使集合保持不变并可在循环后重复使用。

fn main() {
    let names = vec!["Bob", "Frank", "Ferris"];

    for name in names.iter() {
        match name {
            &"Ferris" => println!("There is a rustacean among us!"),
            // TODO ^ Try deleting the & and matching just "Ferris"
            _ => println!("Hello {}", name),
        }
    }
    
    println!("names: {:?}", names);
}

into_iter- 这会消耗集合,以便在每次迭代时提供准确的数据。一旦集合被消耗,它就不再可用于重用,因为它已在循环中“移动”。

fn main() {
    let names = vec!["Bob", "Frank", "Ferris"];

    for name in names.into_iter() {
        match name {
            "Ferris" => println!("There is a rustacean among us!"),
            _ => println!("Hello {}", name),
        }
    }
    
    println!("names: {:?}", names);
    // FIXME ^ Comment out this line
}

iter_mut - 这会可变地借用集合的每个元素,允许就地修改集合。

fn main() {
    let mut names = vec!["Bob", "Frank", "Ferris"];

    for name in names.iter_mut() {
        *name = match name {
                   &mut "Ferris" => "There is a rustacean among us!",
            _ => "Hello",
        }
    }

    println!("names: {:?}", names);
}

match

Rust 通过match关键字提供模式匹配,它可以像 C 一样使用switch。评估第一个匹配臂,并且必须涵盖所有可能的值。
结构元组,结构体
对于指针,需要区分解构和解引用,因为它们是不同的概念,与C.

取消引用用途 *
解构使用&, ref, 和ref mut

结构

卫兵:编译器不会检查任意表达式是否已检查所有可能的条件。因此,您必须_在最后使用模式。
捆绑:
if let
while let

function

方法

方法是附加到对象的函数。这些方法可以通过self关键字访问对象及其其他方法的数据。方法在impl块下定义。

关闭

闭包是可以捕获封闭环境的函数。例如,一个捕获 x 变量的闭包:

|val| val + x
闭包的语法和功能使其非常便于即时使用。调用闭包就像调用函数一样。但是,可以推断输入和返回类型,并且必须指定输入变量名称。

闭包的其他特征包括:

使用||而不是()围绕输入变量。
{}单个表达式的可选正文分隔 ( )(否则为强制性)。
捕获外部环境变量的能力。

模块

能见度

默认情况下,模块中的项目具有私有可见性,但这可以用pub修饰符覆盖。只有模块的公共项可以从模块范围之外访问。

结构可见性

结构对其字段具有额外的可见性。可见性默认为私有,并且可以用pub修饰符覆盖。这种可见性仅在从定义它的模块外部访问结构时才重要,并且具有隐藏信息(封装)的目标。

该use声明

该use声明可用于将完整路径绑定到新名称,以便于访问。

super 和 self

的super和self关键字可以在路径中使用访问的项目时,以除去模糊并防止不必要的路径硬编码。

文件层次结构

模块可以映射到文件/目录层次结构。让我们分解文件中的 可见性示例:

Crates板条箱

cargo

依赖关系
公约
测试
构建脚本

属性

死码

crates

该crate_type属性可用于告诉编译器一个 crate 是二进制文件还是库(甚至是哪种类型的库),该crate_name 属性可用于设置 crate 的名称。

配置文件

某些条件,例如target_os隐式提供rustc,但自定义条件必须传递给rustc使用–cfg标志。

泛型

范围规则

作用域在所有权、借用和生命周期中扮演着重要的角色。也就是说,它们向编译器指示何时借用有效、何时可以释放资源以及何时创建或销毁变量。

RAII

Rust 中的变量不仅仅在堆栈中保存数据:它们还拥有 资源,例如Box拥有堆中的内存。Rust 强制执行RAII (资源获取即初始化),因此只要对象超出范围,就会调用其析构函数并释放其拥有的资源。

此行为可防止资源泄漏错误,因此您永远不必手动释放内存或再次担心内存泄漏!

所有权和搬家

因为变量负责释放自己的资源, 资源只能有一个 owner。这也可以防止资源被多次释放。请注意,并非所有变量都拥有资源(例如引用)。

在进行赋值 ( let x = y) 或按值 ( foo(x))传递函数参数时,资源的所有权将被转移。在 Rust 中,这被称为移动。

移动资源后,不能再使用以前的所有者。这避免了创建悬空指针。

可变性

当所有权转移时,数据的可变性可以改变。

部分动作

在单个变量的解构中,可以同时使用by-move和 by-reference模式绑定。这样做将导致变量的部分移动,这意味着变量的一部分将被移动,而其他部分将保持不变。在这种情况下,父变量不能作为一个整体使用,但是仍然可以使用仅引用(而不是移动)的部分。

借贷

大多数情况下,我们希望在不拥有所有权的情况下访问数据。为了实现这一点,Rust 使用了借用机制。T可以通过引用 ( &T)传递对象,而不是按值 ( )传递对象。

编译器静态地保证(通过其借用检查器)引用 始终指向有效对象。也就是说,虽然存在对对象的引用,但该对象不能被销毁。

  • 可变性
    • 可以使用 可变地借用可变数据&mut T。这称为可变引用,并为借用者提供读/写访问权限。相反,&T通过不可变引用借用数据,借用者可以读取数据但不能修改它
  • 别名
    • 数据可以不可变地借用任意次,但在不可变地借用的同时,不能可变地借用原始数据。另一方面,一次只 允许一个可变借用。只有在最后一次使用可变引用后才能再次借用原始数据。
  • 参考模式
    • 当通过let绑定进行模式匹配或解构时,ref 关键字可用于获取对结构/元组字段的引用。下面的示例显示了一些可能有用的实例

lifetime

寿命是一个构建体中的编译器(或更具体地,其借检查器)使用,以确保所有借位是有效的。具体来说,变量的生命周期从创建时开始,到销毁时结束。虽然生命周期和作用域经常一起被引用,但它们并不相同。

以我们通过 借用变量的情况为例&。借用的生命周期由它的声明位置决定。因此,只要在贷方被销毁之前结束,借入就是有效的。但是,借用的范围取决于引用的使用位置。

trait

宏规则!

代号

宏的参数以美元符号为前缀$,类型用指示符注释:

错误处理

panic

我们将看到的最简单的错误处理机制是panic. 它会打印一条错误消息,开始展开堆栈,然后通常会退出程序。在这里,我们明确调用panic我们的错误条件:

标准库类型

你可能感兴趣的:(2021-07-13)