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循环!

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

相关推荐
现在就干24 分钟前
Spring事务基础:你在入门时踩过的所有坑
java·后端
该用户已不存在37 分钟前
Gradle vs. Maven,Java 构建工具该用哪个?
java·后端·maven
JohnYan1 小时前
Bun技术评估 - 23 Glob
javascript·后端·bun
二闹1 小时前
聊天怕被老板发现?摩斯密码来帮你
后端·python
用户298698530141 小时前
# C#:删除 Word 中的页眉或页脚
后端
David爱编程1 小时前
happens-before 规则详解:JMM 中的有序性保障
java·后端
小张学习之旅1 小时前
ConcurrentHashMap
java·后端
PetterHillWater2 小时前
阿里Qoder的Quest小试牛刀
后端·aigc
程序猿阿伟2 小时前
《支付回调状态异常的溯源与架构级修复》
后端·架构
dreams_dream2 小时前
django错误记录
后端·python·django