Rust第四天,Rust中常见编程概念

接下来学习变量和常量、基本类型、函数、注释和控制流。我们一个一个详细来看。

变量和常量

我们先看个例子:

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

你们说上面的内容运行之后会不会报错,以前端的角色来看let不就是定义变量的吗? 当然不会报错了。可是这里是Rust哈哈哈。果断在x=6这里报错。这是为什么呢?

因为这里的变量引出了一个叫可变性。变量与可变性,就是说定义了一个变量默认是不可变的,只有加上可变性属性才可以随意该同类型的值。那么我们该如何修改上面的代码呢? 如下:

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

就是mut来标记我们变量的可变性。 当然有变量肯定也就有常量了,那么常量我们怎么定义呢?

Rust 复制代码
fn main(){
   const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
}

我们运行之后会有以下内容:

变量的作用域

ini 复制代码
fn main() {
    let x = 5;

    let x = x + 1;

    {
        let x = x * 2;
        println!("The value of x in the inner scope is: {x}");
    }

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

我们看上面这个例子,你觉得控制台会打印出什么?

The value of x in the inner scope is: 12

The value of x is: 6

这次猜对了,和其他编程语言一样是有作用域的。最后附上我们的运行截图:

在Rust中变量有一个区别就是,不能自动转换变量的数据类型。如下所示:

rust 复制代码
    fn main(){
        let spaces = "   ";
        let spaces = spaces.len();
    }

运行上面内容就会报错,为什么报错,因为是没有加mut可变性吗?加上可变性如下:

rust 复制代码
fn main(){
    let mut spaces = "   ";
    spaces = spaces.len();
}

运行结果还是报错。怎么回事呢?原来是在Rust中不能自动改变变量的数据类型。因为len()方法是作用在数字上的。字符类型不一样使用肯定报错。

基本类型

由于上面的数据类型不能自动变化的特性,所以我们Rust是静态类型语言。我们再来详细看一下另外一个例子:

rust 复制代码
fn main(){
    let guess: u32 = "42".parse().expect("Not a number!");
    println!("The value of guess is: {guess}");
}

上面我们给guess设置了u32也就是说无符号的数字类型,假如不添加变量的类型就会报错。接下来我们就仔细讲一下在Rust中有哪些常见的数据类型:整数、浮点数、布尔值和字符

整数

长度 已签名 未签名
8位 i8 u8
16位 i16 u16
32位 i32 u32
64位 i64 u64
128位 i128 u128
依赖于架构 isize usize

每个带符号变体可以存储从 −(2 n − 1 ) 到 2 n − 1 − 1 (含)之间的数字,其中n 是该变体使用的位数。因此,an i8可以存储从 −(2 7 ) 到 2 7 − 1 之间的数字,相当于 −128 到 127。无符号变体可以存储从 0 到 2 n − 1 之间的数字,因此 au8可以存储从 0 到 2 8 − 1 之间的数字,相当于 0 到 255。

整数溢出

假设你有一个类型的变量u8,其值可以保存在 0 到 255 之间。如果你尝试将该变量更改为该范围之外的值(例如 256),就会发生整数溢出 ,这可能导致以下两种行为之一。在调试模式下编译时,Rust 会检查整数溢出,如果发生这种情况,程序会在运行时崩溃 。Rust 使用术语 "恐慌"来表示程序因错误退出

当您使用该--release标志在发布模式下进行编译时,Rust 不会 检查会导致恐慌的整数溢出。相反,如果发生溢出,Rust 会执行二进制补码包装 。简而言之,大于类型可以容纳的最大值的值会"包装"到类型可以容纳的最小值。对于u8,值 256 变为 0,值 257 变为 1,依此类推。程序不会恐慌,但变量的值可能不是您期望的值。依赖整数溢出的包装行为被视为错误。

浮点数

我们来回看一个浮点数类型的例子,如下所示:

csharp 复制代码
fn main() {
    let x = 2.0; // f64
    let y: f32 = 3.0; // f32
}

Rust 的浮点类型是f32f64,它们的大小分别为 32 位和 64 位。默认类型是 ,f64 因为在现代 CPU 上,它的速度与CPU大致相同,f32但能够实现更高的精度.

接着我们来看一下如何进行数值运算。如下面例子:

rust 复制代码
fn main() {
    // addition
    let sum = 5 + 10;

    // subtraction
    let difference = 95.5 - 4.3;

    // multiplication
    let product = 4 * 30;

    // division
    let quotient = 56.7 / 32.2;
    let truncated = -5 / 3; // Results in -1

    // remainder
    let remainder = 43 % 5;
}

这些语句中的每个表达式都使用一个数学运算符,并计算出一个值,然后将其绑定到一个变量

布尔值

布尔类型就两个值,truefalse.我们来看一个例子:

rust 复制代码
fn main() {
    let t = true;

    let f: bool = false; // with explicit type annotation
}

字符

Rust 的char类型是该语言最原始的字母类型。以下是一些声明char值的示例:

rust 复制代码
fn main() {
    let c = 'z';
    let z: char = 'ℤ'; // with explicit type annotation
    let heart_eyed_cat = '😻';
}

复合类型

复合类型可以将多个值组合成一种类型。Rust 有两种原始复合类型:元组和数组。

元组

是一种将多个不同类型的值组合成一种复合类型的通用方法。元组的长度是固定的:一旦声明,其大小就无法增加或缩小。 看一个例子:

rust 复制代码
fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
}

那么我们如何访问tup中的一个值呢,如下我们可以结构:

rust 复制代码
fn main() {
    let tup = (500, 6.4, 1);

    let (x, y, z) = tup;

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

还有没有其他方式呢?如下我们可以通过索引的方式获取:

rust 复制代码
fn main() {
    let x: (i32, f64, u8) = (500, 6.4, 1);

    let five_hundred = x.0;

    let six_point_four = x.1;

    let one = x.2;
}

那么我们如何修改元组中的值呢?如下:

rust 复制代码
fn main() {
    let mut x: (i32, i32) = (1, 2);
    x.0 = 0;
    x.1 += 5;
}

数组

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

rust 复制代码
fn main() { let a = [1, 2, 3, 4, 5]; }

可以使用方括号来编写数组的类型,其中包含每个元素的类型、分号以及数组中元素的数量.

rust 复制代码
let a: [i32; 5] = [1, 2, 3, 4, 5];

这里的 【】中代表的是【类型,数量】。

rust 复制代码
let a = [3; 5];

上面的是初始化一个数组,指定的数组a将包含5所有初始设置为该值的元素 3。这与书写相同,let a = [3, 3, 3, 3, 3];但更简洁。这里的 【】中代表的是【初始值,数量】。

这里我们要注意两种中括号的位置的不同,含义也不同

我们如何访问数组数据呢?如下使用索引获取:

rust 复制代码
fn main() {
    let a = [1, 2, 3, 4, 5];

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

函数

函数在 Rust 代码中非常普遍。您已经了解了该语言中最重要的函数之一:main函数,它是许多程序的入口点。您还了解了fn关键字,它允许您声明新函数。

Rust 代码使用蛇形命名法作为函数和变量名的常规样式,所有字母均小写,并用下划线分隔单词.

rust 复制代码
fn main() {
    println!("Hello, world!");

    another_function();
}

fn another_function() {
    println!("Another function.");
}

运行上面代码,在命令行会输出什么呢?如下所示:

Hello, world! Another function.

当然我们也可以往函数里面传递参数了,那么如何传递参数呢?我们请看下面代码:

rust 复制代码
fn main() { 
    another_function(5); 
} 
fn another_function(x: i32) { 
      println!("The value of x is: {x}"); 
}

运行上面代码我们将可以得到:The value of x is: 5,这里我们要注意每次在声明函数参数的时候必须要声明参数的类型,像上面的i32就是x的参数类型。 那么我们要传入多个参数怎么写呢?别慌我们可以看看下面代码:

rust 复制代码
fn main() {
    print_labeled_measurement(5, 'h');
}

fn print_labeled_measurement(value: i32, unit_label: char) {
    println!("The measurement is: {value}{unit_label}");
}

运行上面代码我们会得到:The measurement is: 5h.下面我们来讲一个重要的概念: [语句和表达式]

语句和表达式

我们可以自己试着运行下面代码:

rust 复制代码
fn main() { 
    let y = { let x = 3; x + 1 }; 
    println!("The value of y is: {y}"); 
}

注意上面内容我们运行之后控制台会打印:The value of y is: 4.这是什么原因呢?

原因是let x = 3; x + 1这里的x+1没有分号,所以这里并不是一个表达式,而是一个语句。意思就是将x+1这个语句的结果赋值给y.

另一种情况,假如我们将x+1改为x+1;的话会发生什么?

如上图,报错啦,原因是什么呢?x+1;是一个语句,不是表达式了就不能够将值赋给y了。语句不返回任何值。

那么我们就仔细来弄明白什么叫"具有返回值的函数"吧!

具有返回值的函数

rust 复制代码
fn five() -> i32 {
    5
}

fn main() {
    let x = five();

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

像上面代码中的内容,在函数后面加->和其返回值类型,就可以认定是有返回值的函数。运行上面代码我们会得到:The value of x is: 5.

我们再来看一个例子:

rust 复制代码
fn main() {
    let x = plus_one(5);

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

fn plus_one(x: i32) -> i32 {
    x + 1
}

运行上面代码会输出什么内容?会输出The value of x is: 6,因为plus_one函数是个表达式不是语句,假如在x + 1后面加上分号,将会报错,因为加上分号就变成了语句,然而语句不返回任何内容。所以就会报错!

注释

Rust中的注释也很简单,下面看几种常见的注释方式:

注释方式一

rust 复制代码
// 这里是我们的单行注释

// 这里是
// 我们多
// 行注释

/*
    这里是
    我们多
    行注释
*/

上面就是在Rust的注释了,单行和多行注释,都可以用在语句上方、下方或者语句后面的。

控制流

控制流也就是我们的流程控制类似在javascript中的ifelseswitch等,我们来看一下在Rust中如何来使用流程控制吧!

rust 复制代码
fn main() {
    let number = 3;

    if number < 5 {
        println!("condition was true");
    } else {
        println!("condition was false");
    }
}

上面代码我们定义了一个变量number,然后去用if判断。可以看出在Rust中的流程控制也是用if ...else....运行之后返回:condition was true,假如我们将上面number改为7,就会打印另外一句话了。

这里我们注意,由于Rust是静态类型的语言,所以if条件后面跟着的变量类型必须是boolean,如果不是Boolean就会报错。那么我们如何将数字转换为一个boolean值呢?如下:

rust 复制代码
fn main() {
    let number = 3;

    if number != 0 {
        println!("number was something other than zero");
    }
}

可以使用运算符,两遍是否相等,如果为真就运行大括号里面的代码,为否就跳过运行。

当然了有的小伙伴可能会说,我有一个学生成绩,按照区间划分等级。你这怎么实现,这里我们绝不使用判断里面套判断。Rust给我们提供了一个else分支。如下所示:

rust 复制代码
fn main() {
    let number = 6;

    if number % 4 == 0 {
        println!("number is divisible by 4");
    } else if number % 3 == 0 {
        println!("number is divisible by 3");
    } else if number % 2 == 0 {
        println!("number is divisible by 2");
    } else {
        println!("number is not divisible by 4, 3, or 2");
    }
}

看完就知道如何使用else分支了吧!

在if中使用let

因为if是一个表达式,我们可以在语句的右侧使用它let 来将结果赋给变量。之前我们说了什么是表达式什么是语句。我们来看一下如何使用:

rust 复制代码
fn main() {
    let condition = true;
    let number = if condition { 5 } else { 6 };

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

上面打印出来就是:The value of number is: 5.这里要注意以下number的类型一定要统一,也就是说else两边的内容都要是同一种数据类型,像下面这段代码运行就会报错。

ini 复制代码
fn main() {
    let condition = true;

    let number = if condition { 5 } else { "six" };

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

上图就是我们说的值类型不一致的情况。是不是报错了?

循环

我们接着来看看循环是个怎么回事,很简单循环就是loop,while,for.让我们来看一段代码:

rust 复制代码
fn main() {
    loop {
        println!("again!");
    }
}

运行上面代码就会在终端一直打印again,这不是八阿哥,我们按住ctrl+c停止掉就行了。遇事不慌哈!

那么我们如何在循环中返回值呢?

从循环返回值

rust 复制代码
fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

    println!("The result is {result}");
}

上面代码中的break就是在循环中返回某个值。所以最后值肯定是20了。

[循环标签用于消除多个循环之间的歧义]

如果循环内有循环,break并且continue此时应用于最内层循环,则可以选择在循环上指定循环标签 break,然后将其与或 一起使用continue,以指定这些关键字应用于带标签的循环,而不是最内层循环。循环标签必须以单引号开头。

rust 复制代码
fn main() {
    let mut count = 0;
    'counting_up: loop {
        println!("count = {count}");
        let mut remaining = 10;

        loop {
            println!("remaining = {remaining}");
            if remaining == 9 {
                break;
            }
            if count == 2 {
                break 'counting_up;
            }
            remaining -= 1;
        }

        count += 1;
    }
    println!("End count = {count}");
}

[条件循环while]

rust 复制代码
fn main() {
    let mut number = 3;

    while number != 0 {
        println!("{number}!");

        number -= 1;
    }

    println!("LIFTOFF!!!");
}

这里的whilejavascript的差不多,不做过多讲解啦!

for循环遍历

ini 复制代码
fn main() {
    let a = [10, 20, 30, 40, 50];
    let mut index = 0;

    while index < 5 {
        println!("the value is: {}", a[index]);

        index += 1;
    }
}

我们在循环这块比较常用的还是for循环!

好了,感谢大家观看,有缘再见!

相关推荐
㳺三才人子5 小时前
初探 Flask
后端·python·flask·html
星栈独行5 小时前
我在 Rust 全栈项目里用 JWT 做无状态认证
开发语言·后端·rust·前端框架·开源·github·web
Java爱好狂.5 小时前
Java程序员体系化学习路线(2026最新版)
java·后端·java面试·java架构师·java程序员·java八股文·java学习路线
陈随易6 小时前
Redis 8.8发布,一定要更新
前端·后端·程序员
装不满的克莱因瓶6 小时前
SpringBoot 如何将 lib 目录中jar包打包进最终的jar包里面
spring boot·后端·maven·jar·mvn
ltl7 小时前
Transformer 原论文实验结果:为什么 28.4 BLEU 足以改写路线图
后端
excel7 小时前
为什么我推荐使用 Termius:现代 SSH 工具的完整体验
前端·后端
卷毛的技术笔记8 小时前
Java后端硬核实战:用Spring AI Alibaba+Redis给LLM装上“超强记忆中枢”
java·人工智能·redis·后端·spring·ai·系统架构
Vallelonga8 小时前
Rust Conversion 工具 trait AsRef AsMut
开发语言·rust
Vallelonga8 小时前
Rust 中的“解引用”和智能指针与 MutexGuard 等
开发语言·rust