Rust 基础

文章目录

  • 一、变量
    • [1.1 不可变变量/可变变量/常量](#1.1 不可变变量/可变变量/常量)
    • [1.2 变量的可覆盖性](#1.2 变量的可覆盖性)
  • 二、数据类型
    • [2.1 数据类型 & 编译器自动推导机制](#2.1 数据类型 & 编译器自动推导机制)
    • [2.2 标量与复合](#2.2 标量与复合)
  • 三、函数
    • [3.1 普通函数](#3.1 普通函数)
    • [3.2 匿名函数/闭包](#3.2 匿名函数/闭包)
    • [3.3 函数指针](#3.3 函数指针)
    • [3.4 高阶函数](#3.4 高阶函数)
    • [3.5 函数部分完整代码:](#3.5 函数部分完整代码:)

一、变量

1.1 不可变变量/可变变量/常量

Rust 既支持静态变量也就是不可变的变量,也支持普通的可变变量,声明变量使用 let 关键字,使用 let 声明的变量就是不可变的;再加上 mut 关键字就成了可变变量;

测试一下:

可以看到对let 变量再次赋值时编译器会给出错误提示; 而 mut 变量则不会;

其实"不可变的变量"和常量是一个意思,但是没管他叫常量是因为 Rust 中除了不可变变量还有常量的概念,对应的关键字是 const
常量不光默认不可变,它总是不可变,也就是常量无法通过 mut 关键字改变属性,这也就是常量和不可变变量的区别。声明常量使用 const 关键字而不是 let,并且 必须 注明值的类型。

,常量在声明它的整个作用域之中都有效,此属性使得常量可以作为多处代码使用的全局范围的值,例如一个游戏中所有玩家可以获取的最高分或者光速。

将遍布于应用程序中的硬编码值声明为常量,也能帮助后来的代码维护人员了解值的意图。如果将来需要修改硬编码值,也只需修改汇聚于一处的硬编码值。

下面这两行用着是一样的

复制代码
let _via = 6; 
const _const_via = 6; 

其实这些概念和C++真的都很像,学过C++的大多数人应该都是简单看一眼就能理解

1.2 变量的可覆盖性

Rust 支持变量的覆盖,也就是可以定义一个与之前变量同名的新变量,此时一般称之为第一个变量被第二个隐藏(Shadowing) 了,这意味着当使用这个变量的名称时,编译器将使用第二个变量。实际上,第二个变量"覆盖"了第一个变量,此时任何使用该变量名的行为中都会视为是在使用第二个变量,直到第二个变量自己也被隐藏或第二个变量的作用域结束。可以用相同变量名称来隐藏一个变量,以及重复使用 let 关键字来多次隐藏,如:

隐藏与将变量标记为 mut 是有区别的。当不小心尝试对变量重新赋值时,如果没有使用 let 关键字,会导致编译时错误。通过使用 let,我们可以用这个值进行一些计算,不过计算完之后变量仍然是不可变的。

mut 与隐藏的另一个区别是,当再次使用 let 时,实际上创建了一个新变量,我们可以改变值的类型,并且复用这个名字。例如,假设程序请求用户输入空格字符来说明希望在文本之间显示多少个空格,接下来我们想将输入存储成数字(多少个空格):

复制代码
    let spaces = "   ";
    let spaces = spaces.len();

关于这个"覆盖"性质我猜测就是遇到再次定义同名变量时就回收前一个变量然后重新 new 一个同名变量,搜了一下 Rust 是有指针的,就把两个变量地址输出对比了一下,发现确实地址不一样,所以我的猜测应该是对的:

二、数据类型

2.1 数据类型 & 编译器自动推导机制

Rust 是 静态类型(statically typed)语言,每一个值都必须属于某一个 数据类型(data type),也就是说在编译时就必须知道所有变量的类型,才能让 Rust 明确数据处理方式。

不过有时根据值及其使用方式,编译器可以推断出我们想要用的类型,比如前面截图上变量声明后面灰色的 i32 usize, 这两个就是变量类型, 灰色表示是编译器自动推导出来的而非开发者主动指定的。

不过如果当一个变量多种类型均有可能时或编译器无法自动推导出类型时,就必须增加类型注解,像这样:

建议写代码时都一定要主动指定数据类型,而不要靠编译器推导;

2.2 标量与复合

Rust 的变量支持两类数据类型子集:标量(scalar)和复合(compound)。

标量(scalar)类型代表一个单独的值,比如前面的代码中的变量都是标量类型的,变量都是只有一种类型。比如整数类型i32, 浮点数类型f64,布尔类型bool等。以下是一些常见的标量类型:

复制代码
* i8: 8位有符号整数
* i16: 16位有符号整数
* i32: 32位有符号整数
* i64: 64位有符号整数
* isize: 平台相关的有符号整数
* u8: 8位无符号整数
* u16: 16位无符号整数
* u32: 32位无符号整数
* u64: 64位无符号整数
* usize: 平台相关的无符号整数
* f32: 32位浮点数
* f64: 64位浮点数
* char: 字符类型
* bool: 布尔类型

注意:
1.isize 和 usize 依赖计算机架构:64 位上是 64 位的,32 位上是 32 位。
2.多种数字类型的数字字面值允许使用类型后缀,例如 57u8 来指定类型,同时也允许使用 _ 做为分隔符以方便读数,例如1_000,它的值与你指定的 1000 相同。
3.当给一个变量赋值为超出它的数据类型数据范围的值时,这种错误称为 "整型溢出"("integer overflow" ), Rust在debug版下遇到这种问题程序会因错误而退出,而release版会进行一种被称为二进制补码 wrapping(two's complement wrapping)的操作。简而言之,比此类型能容纳最大值还大的值会回绕到最小值。

复合类型(Compound types)可以将多个值组合成一个类型。Rust 有两个原生的复合类型:元组(tuple)和数组(array)。例如数组类型[i32; 3],元组类型(i32, f64),以及结构体类型struct Point { x: i32, y: i32 }等。以下是一些常见的复合类型:

复制代码
* [T; N]: 长度为N的数组类型,元素类型为T。数组中的每个元素的类型必须相同。
  Rust 中的数组与一些其他语言中的数组不同,Rust 中的数组长度是固定的。
* (T1, T2, ..., Tn): 元组类型,包含n个元素,元素类型分别为T1, T2, ..., 
  Tn.元组是一个将多个其他类型的值组合进一个复合类型的主要方式。元组长度固定:
  一旦声明,其长度不会增大或缩小。
* struct S { field1: T1, field2: T2, ..., fieldn: Tn }: 结构体类型,包
  含n个字段,字段类型分别为T1, T2, ..., Tn。

代码示例:

三、函数

Rust 中,函数是一段可重用的代码块,用于执行特定任务。

在 Rust 中,函数定义使用 fn 关键字,后跟参数列表和函数名以及返回值类型(可缺省)。参数列表中的每个参数都要有一个类型注解。函数体以大括号{}包围,可以包含多个语句。如:

复制代码
fn function_name(param1: Type1, param2: Type2) -> ReturnType {
    // TODO 
}

3.1 普通函数

通过输入 fn 后面跟着函数名和一对圆括号来定义函数,大括号标明函数体的开始和结尾。

需要注意的是在 Rust 中,函数的返回值等同于函数体最后一个表达式的值。使用 return 关键字和指定值,可从函数中提前返回;但大部分函数隐式的返回最后的表达式。

这里提一下**"表达式" 与 "语句" 的差别**:

复制代码
* 语句(Statements)是执行一些操作但不返回值的指令。 
* 表达式(Expressions)计算并产生一个值。让我们看一些例子。

具体示例参考最下面的代码截图中的 add 与 add2 函数的声明与使用;

3.2 匿名函数/闭包

Rust支持匿名函数,也称为闭包。闭包是一个可以捕获其环境中变量的函数。闭包的定义使用||符号,后跟参数列表和函数体。如:

复制代码
    let closure = || {
        // 函数体
    };

具体示例参考最下面的代码截图中的 add3 函数的声明与使用;

3.3 函数指针

Rust支持函数指针,可以将函数作为值传递给其他函数或存储在数据结构中。函数指针的类型为fn() -> ReturnType。

如:

复制代码
fn function_name() -> ReturnType {
    // 函数体
}
let function_pointer: fn() -> ReturnType = function_name; // 将函数赋值给函数指针

具体示例参考最下面的代码截图中的 add4 函数的声明与使用;

3.4 高阶函数

高阶函数是指接受其他函数作为参数或返回其他函数作为结果的函数。在Rust中,可以使用泛型来实现高阶函数。

具体示例参考如下的代码截图中的 add5 函数的声明与使用:

3.5 函数部分完整代码:

Rust 复制代码
// 定义一个接受两个整数参数并返回它们的和的函数:
fn add(a: i32, b: i32) -> i32 {
    return a + b
}
fn add2(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    println!("Hello, world!");

    // 普通函数测试
    { 
        let _add_result : i32= add(1, 2);
        let _add_result1 : i32= add2(1, 2);
        println!("函数add结果: {_add_result}; 不指定返回值时add函数结果:{_add_result1} ");
    }

    // 匿名函数(闭包)
    {
        // 定义一个闭包,它接受两个整数参数并返回它们的和
        let add3 = |a: i32, b: i32| a + b;
        let _add_result : i32 = add3(1, 2);
        println!("闭包函数add3结果: {_add_result}; ");
    }

    // 函数指针
    {
        // 定义一个返回和的函数,并将其地址赋给一个函数指针:
        fn add4(a: i32, b: i32) -> i32 {
            a + b
        }
        let add4_ptr: fn(i32, i32) -> i32 = add4;
        let _add_result : i32 = add4_ptr(1, 2);
        println!("函数指针add4结果: {_add_result}");
    }

    // 高阶函数
    {
        // 定义一个高阶函数,接受一个函数作为参数,并返回一个新的函数
        fn high_function(func: impl Fn(i32, i32) -> i32) -> impl Fn(i32, i32) -> i32 {
            move |a, b| func(a, b)
        }

        // 定义一个简单的函数,用于计算两个整数的和
        fn add5(a: i32, b: i32) -> i32 {
            a + b
        }

        // 使用高阶函数
        let doubled_add = high_function(add5);
        let _add_result : i32 = doubled_add(1, 2);
        println!("高阶函数调用add5结果: {_add_result}");
    }
}
相关推荐
重生之后端学习1 分钟前
day08-Elasticsearch
后端·elasticsearch·spring cloud·中间件·全文检索·jenkins
lightqjx4 分钟前
【数据结构】顺序表(sequential list)
c语言·开发语言·数据结构·算法
巨人张14 分钟前
信息素养Python编程题
开发语言·python
志辉AI编程36 分钟前
别人还在入门,你已经精通!Claude Code进阶必备14招
后端·ai编程
阿猿收手吧!38 分钟前
【计算机网络】HTTP1.0 HTTP1.1 HTTP2.0 QUIC HTTP3 究极总结
开发语言·计算机网络
JAVA学习通38 分钟前
图书管理系统(完结版)
java·开发语言
代码老y43 分钟前
Spring Boot项目中大文件上传的高级实践与性能优化
spring boot·后端·性能优化
paishishaba1 小时前
处理Web请求路径参数
java·开发语言·后端
七七七七071 小时前
C++类对象多态底层原理及扩展问题
开发语言·c++