Rust通用编程概念

这篇具有很好参考价值的文章主要介绍了Rust通用编程概念。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

变量与可变性

变量与可变性

在Rust中,声明变量使用let关键字,并且默认情况下,声明的变量是不可变的,要使变量可变需要在声明变量时,在变量前面加上mut关键字。如下:

fn main() {
    let mut x = 10;
    println!("x = {}", x); //x = 10
    x = 20;
    println!("x = {}", x); //x = 20
}

如果将上述代码中的mut关键字去掉,那么在编译代码时就会报错,报错结果就是不能对不可变的变量进行二次赋值,也就是不能对不可变的变量进行修改。如下:

Rust通用编程概念,Rust,rust,开发语言,后端

常量

  • 常量(constant)与不可变的变量一样,在绑定值以后也是不可变的。
  • 在程序在运行期间,常量在其声明的作用域内一直有效。

需要注意的是,Rust里常量使用全大写字母,每个单词之间用下划线分开。如下:

const MAX_POINTS: u32 = 100_000;

常量与不可变的变量的区别:

  • 常量不可以使用mut关键字,因为常量永远都是不可变的。
  • 声明常量使用const关键字,并且常量的类型必须被显示标注。
  • 常量可以在任何作用域内进行声明,包括全局作用域。
  • 常量只能绑定到常量表达式,无法绑定到函数的调用结果或只能在运行时才能计算出的值。
注:可以在数字字面值中插入下划线来增强可读性。

隐藏(Shadowing)

在Rust中,可以使用相同的名字声明新的变量,新的变量会隐藏之前声明的同名变量,在后续的代码中这个变量名代表的就是新的变量。如下:

fn main() {
    let x = 10;
    let x = x + 1;
    println!("x = {}", x); //x = 11
}

此外,使用mut关键字声明变量x也可以达到上述效果。如下:

fn main() {
    let mut x = 10;
    x = x + 1;
    println!("x = {}", x); //x = 11
}

但隐藏和把变量声明为mut是不同的:

  • 在第一份代码中,使用let声明的同名新变量是不可变的,而第二份代码中变量x是可变的。
  • 对于隐藏来说,使用let声明的同名新变量的类型可以与之前的类型不同。

比较常见的使用场景就是数字字符串解析,隐藏可以让解析后的整型复用之前数字字符串的变量名。如下:

fn main() {
    let spaces = "    ";
    let spaces = spaces.len();
    println!("spaces = {}", spaces); //spaces = 4
}

如果Rust没有隐藏这个特性,那么我们就需要重新取一个变量名来避免命名冲突。如下:

fn main() {
    let spaces_str = "    ";
    let spaces_num = spaces_str.len();
    println!("spaces_num = {}", spaces_num); //spaces_num = 4
}

数据类型

数据类型

  • Rust是静态编程语言,在编译时必须知道所有变量的类型,而基于变量使用的值,编译器通常能够推断出变量的具体类型。
  • 如果变量可能的类型比较多,就必须显示标注出变量的类型,否则编译器就会报错。

例如将字符串类型转换成整型的parse方法,就必须添加类型的标注,因为parse方法的解析结果可以是多种数值类型。如下:

fn main() {
    let year: u32 = "2023".parse().expect("无法解析为整数类型!"); //显示标注变量类型
    println!("year = {}", year); //year = 2023
}

标量类型

标量类型

  • 标量类型代表一个单独的值。
  • Rust有四种基本的变量类型:整数类型、浮点类型、布尔类型、字符类型。

整数类型

整数类型

Rust中的整数类型如下:

长度 有符号 无符号
8-bit i8 u8
16-bit i16 u16
32-bit i32 u32
64-bit i64 u64
128-bit i128 u128
arch isize usize

说明一下:

  • 无符号整数类型以u开头,有符号整数类型以i开头,u或i后面的数字表示整数的长度。
  • 有符号整数的取值范围: − 2 n − 1 -2^{n-1} 2n1 2 n − 1 − 1 2^{n-1}-1 2n11,无符号整数的取值范围:0 到 2 n − 1 2^n-1 2n1

isize和usize类型

isize和usize类型的位数由程序运行的计算机的架构所决定:

  • 如果是64位计算机,那么isize和usize就相当于i64和u64。
  • 如果是32为计算机,那么isize和usize就相当于i32和u32。
注:isize和usize通常用于对集合进行索引操作。

整数字面值

  • 除了byte类型外,所有的字面值都允许使用类型后缀。例如57u8。
  • 如果不太清楚应该使用哪种类型,可以使用Rust相应的默认类型。
  • 整数类型的默认类型就是i32,该类型在大部分情形下都是运算速度最快的,即使是在64位系统中。

整数字面量的表示形式:

整数字面量 示例
Decimal(十进制) 98_222
Hex(十六进制) 0xff
Octal(八进制) 0o77
Binary(二进制) 0b1111_0000
Byte(单字节字符)(仅限于u8) b’A’

可以尝试将以上示例的值进行打印,结果如下:

fn main() {
    let a = 98_222;
    let b = 0xff;
    let c = 0o77;
    let d = 0b1111_0000;
    let e = b'A';
    let f = 12f64;

    println!("a = {}", a); //a = 98222
    println!("b = {}", b); //b = 255
    println!("c = {}", c); //c = 63
    println!("d = {}", d); //d = 240
    println!("e = {}", e); //e = 65
}
注:可以在整数字面量中添加下划线来增强可读性。

整数溢出

  • 由于整数是保存在内存中一个固定长度的空间的,因此它能存储的最大值和最小值是固定的,如果我们尝试存储一个数,而这个数又大于该整数类型所能存储的最大值,那么就会导致整数溢出。
  • 在调试模式下编译,Rust会检查整数溢出,如果发生溢出,程序在运行时就会触发panic;在发布模式下编译,Rust不会检查可能触发panic的整数溢出,而会进行二进制补码环绕。

例如u8类型的范围是0-255,如果将u8变量的值更新为256,那么就会导致整数溢出。如下:

fn main() {
    let a: u8 = 255;
    let b: u8 = 1;
    println!("a = {:?}, b = {:?}", a, b);

    println!("a + b = {:?}", a + b); //a + b = 0
}

上述代码在debug模式和release模式下均能成功编译。如下:

Rust通用编程概念,Rust,rust,开发语言,后端

由于该代码会导致整数溢出,因此在debug模式生成的可执行程序在运行时会触发panic,而release模式生成的可执行程序会进行二进制补码环绕,使得256变成了0。如下:

Rust通用编程概念,Rust,rust,开发语言,后端

浮点类型

浮点类型

  • Rust中有两种基础的浮点类型,分别是f32(单精度浮点数)和f64(双精度浮点数)。
  • Rust中的浮点数类型采用IEEE-754标准来表述,其中f64是默认的浮点数类型,因为在现代CPU上f64和f32的运行效率差不多,但f64的精度更高。

例如下面声明的x变量默认是f64类型,而y变量则是我们标注的f32类型。如下:

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

    let y: f32 = 0.3; //f32
}

数值运算

对于所有的数值类型,Rust都支持常见的加、减、乘、除、余等数值运算。如下:

fn main() {
    let sum = 10 + 20;

    let dif = 10.1 - 20.2;

    let mul = 10 * 20;

    let div = 10.1 / 20.2;

    let rem = 10 % 20;
}

布尔类型

布尔类型

Rust中的布尔类型可以是true或false,占据单个字节大小。如下:

fn main() {
    let a = true; //bool
    
    let b: bool = false; //bool
}

字符类型

字符类型

  • Rust语言中的char类型被用来描述语言中最基础的单个字符,字符类型的字面值使用单引号。
  • Rust中的字符类型占用4字节大小,是Unicode标量值,可以表示比ASCII更多的字符内容,比如拼音、中日韩文、emoji表情等。

例如下面的英文字母、中文汉字以及emoji表情都是字符类型。如下:

fn main() {
    let x = 'a'; //char

    let y: char = 'ℤ'; //char

    let z = '😁'; //char

    println!("{} {} {}", x, y, z);
}
注:Unicode标量值范围为U+0000到U+D7FF,以及U+E000到U+10FFFF。

复合类型

复合类型

  • 复合类型可以将多个不同类型的值组合为一个类型。
  • Rust提供了两种内置的复合类型:元组(tuple)、数组(array)。

元组

元组

  • 元组可以将多个不同类型的多个值组合进一个复合类型中。
  • 元组的长度是固定的,一旦声明就无法改变。

声明元组时将一系列值使用逗号分隔后,放置到一对圆括号中即可。如下:

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

说明一下:

  • 声明元组时也可以标注类型,标注时使用一个圆括号,在圆括号中依次标注元组中对应位置的元素类型即可。
  • 不带任何值的元组叫做单元(unit)元组,这种值以及对应的类型都写作(),表示空值或空的返回类型。(如果表达式不返回任何其他值,则会隐式返回单元值)

访问元组元素

访问元组中的元素使用点标记法,在点的后面指明元素的索引号即可。如下:

fn main() {
    let tup: (i32, f64, u8) = (32, 6.4, 8);

    println!("{} {} {}", tup.0, tup.1, tup.2); //32 6.4 8
}

此外,还可以使用模式匹配对元组进行解构(destructuring),从而获取元组中的元素值。如下:

fn main() {
    let tup: (i32, f64, u8) = (32, 6.4, 8);

    let (x, y, z) = tup;

    println!("x = {}, y = {}, z = {}", x, y, z); //x = 32, y = 6.4, z = 8
}

数组

数组

  • 数组也可以将多个值组合在一起,但数组中每个元素的类型必须相同。
  • 数组的长度也是固定的,一旦声明就无法改变。

声明元组时将一系列值使用逗号分隔后,放置到一对中括号中即可。如下:

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

说明一下:

  • 数组的类型表示为[type; len],比如上述代码中数组a的类型为[i32; 5]

此外,如果数组中每个元素的值相同,那么可以以[val; len]的方式声明数组,表示声明的数组中有len个元素,每个元素的值都是val。如下:

fn main() {
    let a = [3; 5]; //等价于let a = [3, 3, 3, 3, 3];
}

访问数组元素

访问数组中的元素使用索引的方式,在中括号中指明元素的索引号即可。如下:

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

    let first = a[0]; //1
    let second = a[1]; //2
}

访问数组元素时,指明的索引值如果超出了数组的范围。如下:

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

    let index = 6;
    let num = a[index]; //error
    println!("num = {}", num);
}

对于上面这种简单的代码逻辑,编译时会检查出越界并产生报错。如下:

Rust通用编程概念,Rust,rust,开发语言,后端

但如果让用户输入一个数字,作为访问数组元素时的索引值。如下:

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

    println!("请输入一个索引:");
    let mut index = String::new();
    io::stdin().read_line(&mut index).expect("无法读取行!");
    let index: usize = index.trim().parse().expect("无法解析为整数类型!");

    let num = a[index];
    println!("num = {}", num);
}

那么在编译时是不会产生报错的,因为我们不知道程序运行后用户输入的数字是多少,但在运行程序后如果发现索引值越界,那么就会触发panic。如下:

Rust通用编程概念,Rust,rust,开发语言,后端

函数

函数声明

  • 声明函数使用fn关键字,fn关键字后面是函数名。
  • 函数名后是一个小括号,小括号里面可以声明函数的参数。
  • 用一对花括号来标识函数体的开始和结尾。

下面的代码中定义了主函数和一个名为another_function的函数,并在主函数中调用了another_function函数。如下:

fn main() {
    println!("main function");
    another_function();
}

fn another_function() {
    println!("another function");
}

说明一下:

  • 在Rust中,函数名和变量名均使用snack case命名规范,即以小写字母命名,多个单词之间使用下划线分开。
  • another_function函数的声明不用在main函数之前,只要another_function的定义对于使用区域是可见的即可。

函数参数

  • 如果函数有参数,那么在函数签名里必须声明每个参数的类型。
  • 函数签名中声明的参数叫做parameter,调用函数时传递的参数叫做argument。

下面给another_function定义了两个参数,并在main函数中对其进行了调用。如下:

fn main() {
    another_function(10, 20); //argument
}

fn another_function(x: i32, y: i32) { //parameter
    println!("x = {}, y = {}", x, y);
}

语句与表达式

  • Rust是一个基于表达式的语言,所以它将语句(statement)与表达式(expression)区别为两个不同的概念。
  • 语句是指那些执行操作但不返回值的指令,而表达式则是指会进行计算并产生一个值作为结果的指令。
  • 函数体由若干条语句组成,并可以以一个表达式作为结尾。

例如下面声明变量时使用的代码就是一条语句,甚至下面整个例子本身也是一条语句。如下:

fn main() {
    let x = 10;
}

由于语句是不会返回值的,因此不能一条语句赋值给另一个变量。如下:

fn main() {
    let y = (let x = 10); //error
}

实际我们应该将一个表达式赋值给其他变量,因此编译上述代码你会看到如下报错:

Rust通用编程概念,Rust,rust,开发语言,后端

此外,Rust中的代码块也是一个表达式,表达式的值就是代码块中最后一个表达式的值。如下:

fn main() {
    let y = {
        let x = 1;
        x + 3
    };
    println!("y = {}", y); //y = 4
}

注意:

  • 代码中的x+3后面不能加分号,因为加了分号后x+3;就变成了一个语句,此时该代码块不会返回任何值,或者说代码块返回的是一个单元元组()

函数返回值

  • ->符号后面可以声明函数的返回值类型,但是不可以为返回值命名。
  • 在Rust中,函数的返回值就是函数体中最后一个表达式的值。
  • 可以使用return关键字,并指定一个值来提前从函数中返回。

下面声明了一个plus_five函数,该函数接收一个i32类型的参数,并将该参数的值加5后进行返回。如下:

fn plus_five(val: i32) -> i32 {
    val + 5
}

fn main() {
    let x = plus_five(10);
    println!("x = {}", x); //15
}

注释

注释

Rust中可以通过///**/进行单行或多行注释。如下:

/*
这是一个多行注释
...
...
*/
fn main() {
    //这是一个多行注释
    //...
    //...

    /*这是一个单行注释*/
    println!("Hello World"); //这是一个单行注释
}
注:Rust中还有一种注释叫做文档注释,后续再介绍。

控制流

if表达式

if表达式

  • if表达式允许你根据条件来执行不同的代码分支,但注意这个条件必须是bool类型的。
  • 在if表达式后面可以追加若干个else if表达式,最后还可以追加一个else表达式。

下面的代码中通过if表达式判断一个分数的等级。如下:

fn main() {
    let score = 95;

    if score >= 0 && score < 60 {
        println!("不及格");
    } else if score >= 60 && score < 80{
        println!("良好");
    } else if score >= 80 && score <= 100{
        println!("优秀");
    } else {
        println!("成绩出错");
    }
}

当else if表达式过多时,为了增加代码的可读性,最好使用match表达式对代码进行重构。如下:

fn main() {
    let score = 95;

    match score {
        0..=59 => println!("不及格"),
        60..=79 => println!("良好"),
        80..=100 => println!("优秀"),
        _ => println!("成绩出错"),
    }
}

在let语句中使用if

由于if是一个表达式,因此可以将其放在等号右边用于给其他变量赋值。如下:

fn main() {
    let cond = true;

    let number = if cond { 10 } else { 20 };

    println!("number = {}", number); //number = 10
}

注意:

  • if表达式后面如果跟有else if表达式或else表达式,那么这些代码块的返回值类型必须相同,因为Rust在编译时必须知道接收if表达式结果的变量的类型。

循环

loop循环

  • 使用loop关键字可以让Rust反复执行某一块代码。
  • 与其他语言类似,在loop循环中也可以使用continue和break关键字。
  • loop循环可以返回值,当使用break关键字跳出循环时,在break后面指明返回值即可。

下面代码中,当counter累加到10时,将counter乘2的值作为loop循环的返回值,并用result变量接收了这个返回值。如下:

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;
        if counter == 10 {
            break counter * 2; //loop循环的返回值
        }
    };

    println!("result = {}", result); //result = 20
}

while循环

  • while循环能够在每次执行循环体之前都判断依次条件,如果条件满足则执行循环体。
  • 与其他语言类似,在while循环中也可以使用continue和break关键字。

下面代码中,当number的值减为0时便不再执行循环体。如下:

fn main() {
    let mut number = 3;
    
    while number != 0 {
        println!("{}!", number);
        number -= 1;
    }
    
    println!("LIFTOFF!!!");
}

for循环

  • 使用loop和while可以用来遍历集合,这时需要我们对索引进行控制,比较容易出错并且低效。
  • for循环更简洁紧凑,它可以针对集合中的每个元素来执行一些代码。
  • for循环的安全性和间接性,使它成为了Rust中最常用的循环结构。

下面使用for循环对数组中的元素进行了一次遍历。如下:

fn main() {
    let a = [10, 20, 30, 40, 50];
    for element in a.iter() {
        println!("value: {}", element);
    }
}

说明一下:

  • 数组的iter方法会返回该数组的只读迭代器,for循环则是依次获取数组中每个元素的迭代器,然后以借用的方式获取元素的值,使得我们可以通过element在循环体中对数组中的元素进行访问。

此外,for循环可以搭配Range来对某个数字序列进行遍历。如下:

fn main() {
    for number in (1..4).rev() {
        println!("{}!", number);
    }
    println!("LIFTOFF!!!");
}

说明一下:文章来源地址https://www.toymoban.com/news/detail-715602.html

  • Range是标准库提供的,用来生成从一个数字开始到另一个数字结束之前的所有数字序列。
  • Range中的rev方法可以对Range生成的数字序列进行反转。

到了这里,关于Rust通用编程概念的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Rust 语言常见的一些概念(下)

    目录 1、函数 参数 语句和表达式 具有返回值的函数 2、注释 文档注释 多行注释 3、控制流 3.1 if 表达式 3.2 使用esle if 处理多重条件 3.3 在 let 语句中使用 if 3.4 使用循环重复执行 使用 loop 重复执行代码 从循环中返回值 循环标签:在多个循环之间消除歧义 while 条件循环 使用

    2024年02月06日
    浏览(30)
  • Rust 语言常见的一些概念(上)

    目录 1、变量的可变性 常量  隐藏 2、数据类型 2.1 标量类型 整型 浮点型 数值运算 布尔型 字符类型 复合类型 元组类型 数组类型 变量默认是不可改变的(immutable)。这是 Rust 提供给你的众多优势之一,让你得以充分利用 Rust 提供的安全性和简单并发性来编写代码。不过,你

    2024年02月06日
    浏览(22)
  • Rust编程语言入门之Rust的面向对象编程特性

    Rust 受到多种编程范式的影响,包括面向对象 面向对象通常包含以下特性:命名对象、封装、继承 “设计模式四人帮”在《设计模型》中给面向对象的定义: 面向对象的程序由对象组成 对象包装了数据和操作这些数据的过程,这些过程通常被称作方法或操作 基于此定义:

    2023年04月21日
    浏览(33)
  • 【Rust 基础篇】Rust FFI:连接Rust与其他编程语言的桥梁

    Rust是一种以安全性和高效性著称的系统级编程语言,具有出色的性能和内存安全特性。然而,在现实世界中,我们很少有项目是完全用一种编程语言编写的。通常,我们需要在项目中使用多种编程语言,特别是在与现有代码库或底层系统交互时。为了实现跨语言的互操作性,

    2024年02月15日
    浏览(36)
  • Rust编程语言入门之模式匹配

    模式是Rust中的一种特殊语法,用于匹配复杂和简单类型的结构 将模式与匹配表达式和其他构造结合使用,可以更好地控制程序的控制流 模式由以下元素(的一些组合)组成: 字面值 解构的数组、enum、struct 和 tuple 变量 通配符 占位符 想要使用模式,需要将其与某个值进行

    2023年04月22日
    浏览(75)
  • Rust编程语言入门之智能指针

    指针:一个变量在内存中包含的是一个地址(指向其它数据) Rust 中最常见的指针就是”引用“ 引用: 使用 借用它指向的值 没有其余开销 最常见的指针类型 智能指针是这样一些数据结构: 行为和指针相似 有额外的元数据和功能 通过记录所有者的数量,使一份数据被多个

    2023年04月16日
    浏览(35)
  • Rust编程语言入门之高级特性

    不安全 Rust 高级 Trait 高级 类型 高级函数和闭包 宏 隐藏着第二个语言,它没有强制内存安全保证:Unsafe Rust(不安全的 Rust) 和普通的 Rust 一样,但提供了额外的“超能力” Unsafe Rust 存在的原因: 静态分析是保守的。 使用 Unsafe Rust:我知道自己在做什么,并承担相应风险

    2023年04月24日
    浏览(35)
  • Rust编程语言入门之无畏并发

    Concurrent:程序的不同部分之间独立的执行(并发) Parallel:程序的不同部分同时运行(并行) Rust无畏并发:允许你编写没有细微Bug的代码,并在不引入新Bug的情况下易于重构 注意:本文中的”并发“泛指 concurrent 和 parallel 在大部分OS里,代码运行在进程(process)中,OS同时

    2023年04月19日
    浏览(55)
  • 【跟小嘉学 Rust 编程】十七、面向对象语言特性

    【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学 Rust 编程】六、枚举

    2024年02月10日
    浏览(74)
  • 如何在 macOS 上安装 Rust 编程语言

    安装Rust编程语言在Mac上是一个相对简单的过程,但它可能会涉及多个步骤。在本文中,我将详细说明如何在Mac上安装Rust,并提供一些常见问题的解决方法。请注意,由于软件和工具可能会发生变化,因此建议首先查看Rust官方网站以获取最新的安装说明。 目录 1.打开终端 2

    2024年02月01日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包