青少年编程与数学 02-019 Rust 编程基础 03课题、变量与可变性

news2025/5/13 18:02:21

青少年编程与数学 02-019 Rust 编程基础 03课题、变量与可变性

  • 一、使用多个文件(模块)
    • 1. 创建包结构
    • 2. 在 `main.rs` 中引入模块
        • 示例:`main.rs`
    • 3. 定义模块文件
        • 示例:`module1.rs`
        • 示例:`module2.rs`
    • 4. 定义子模块
        • 示例:`submodule/mod.rs`
        • 示例:`submodule/subfunction.rs`
    • 5. 使用模块
        • 示例:`main.rs`
    • 6. 包的结构和模块路径
    • 7. 使用 `use` 语句简化路径
        • 示例:`main.rs`
    • 模块总结
  • 二、常量
    • 1. 常量的定义
        • 示例:
    • 2. 常量的特点
    • 3. 常量的作用域
        • 示例:
    • 4. 常量的用途
        • 示例:
    • 5. 与变量的区别
    • 6. 常量的限制
        • 示例:
    • 7. 常量的高级用法
        • 示例:
    • 常量总结
  • 三、变量与可变性
    • 1. 变量的声明与初始化
    • 2. 默认不可变性
    • 3. 可变性的作用
        • 示例:不可变变量
        • 示例:可变变量
    • 4. 变量的绑定与阴影
    • 5. 可变性与函数参数
    • 6. 可变性与并发安全
        • 示例:并发中的可变性
    • 7. 示例合并
      • 运行结果
    • 8. 变量与可变性总结
  • 总结

**摘要:**在 Rust 编程中,变量与可变性是核心概念之一。Rust 的设计哲学强调内存安全和并发安全,而变量的可变性规则是实现这些目标的重要机制。理解变量与可变性对于编写安全、高效的 Rust 代码至关重要。字面量、常量的应用虽然较为容易,但在编程过程也是必要知识。

**关键词:**常量、变量、可变性、安全、模块


一、使用多个文件(模块)

在 Rust 中,一个包(crate)可以包含多个源代码文件,而不仅仅是一个 main.rslib.rs 文件。通过合理组织代码结构,可以将功能拆分到多个文件中,提高代码的可读性和可维护性。以下是如何在一个包中使用多个源代码文件的方法。

1. 创建包结构

假设你正在创建一个二进制包(binary crate),其结构可以如下:

my_project/
├── Cargo.toml
├── src/
│   ├── main.rs
│   ├── module1.rs
│   ├── module2.rs
│   └── submodule/
│       ├── mod.rs
│       └── subfunction.rs

2. 在 main.rs 中引入模块

在 Rust 中,模块(module)是组织代码的基本单元。你可以在 main.rs 中使用 mod 关键字引入其他源代码文件。

示例:main.rs
// 引入同一目录下的模块
mod module1;
mod module2;

// 引入子模块
mod submodule;

fn main() {
    // 使用模块中的函数
    module1::function1();
    module2::function2();

    // 使用子模块中的函数
    submodule::subfunction::function3();
}

3. 定义模块文件

每个模块文件对应一个 .rs 文件。在模块文件中,你可以定义函数、结构体、枚举等。

示例:module1.rs
pub fn function1() {
    println!("This is function1 from module1");
}
示例:module2.rs
pub fn function2() {
    println!("This is function2 from module2");
}

4. 定义子模块

子模块可以进一步组织代码。在子模块目录中,必须有一个 mod.rs 文件,它作为子模块的入口。

示例:submodule/mod.rs
// 引入子模块中的文件
mod subfunction;

pub fn submodule_function() {
    println!("This is a function in submodule");
}

// 重新导出子模块中的函数
pub use subfunction::function3;
示例:submodule/subfunction.rs
pub fn function3() {
    println!("This is function3 from subfunction");
}

5. 使用模块

main.rs 中,你可以通过模块路径访问定义在其他文件中的函数或类型。

示例:main.rs
mod module1;
mod module2;
mod submodule;

fn main() {
    module1::function1(); // 调用 module1 中的函数
    module2::function2(); // 调用 module2 中的函数
    submodule::subfunction::function3(); // 调用 submodule/subfunction 中的函数
}

6. 包的结构和模块路径

Rust 使用文件系统结构来组织模块。模块路径由文件路径和 mod 关键字共同决定。以下是一些关键点:

  • 每个 .rs 文件可以定义一个模块。
  • 子模块目录需要包含一个 mod.rs 文件。
  • 在父模块中使用 mod 关键字引入子模块。
  • 使用 pub 关键字使模块、函数或类型对外可见。

7. 使用 use 语句简化路径

在代码中频繁使用长模块路径可能会使代码变得冗长。可以使用 use 语句将模块路径引入到当前作用域中。

示例:main.rs
mod module1;
mod module2;
mod submodule;

// 引入模块中的函数
use module1::function1;
use module2::function2;
use submodule::subfunction::function3;

fn main() {
    function1(); // 直接调用 function1
    function2(); // 直接调用 function2
    function3(); // 直接调用 function3
}

模块总结

通过合理组织代码结构,使用 mod 关键字引入模块,以及使用 use 语句简化路径,你可以在一个 Rust 包中使用多个源代码文件。这种方式不仅有助于代码的模块化,还可以提高代码的可读性和可维护性。

二、常量

在 Rust 中,常量是一种特殊的变量,它的值在程序运行期间不能被修改。常量在 Rust 中有其独特的用途和规则,以下是对 Rust 中常量的详细解析:

1. 常量的定义

在 Rust 中,常量使用 const 关键字定义。其基本语法如下:

const CONSTANT_NAME: Type = value;
  • CONSTANT_NAME:常量的名称,通常使用大写字母和下划线来分隔单词,以符合 Rust 的命名约定。
  • Type:常量的类型,必须明确指定。
  • value:常量的值,必须是一个编译时可知的值。
示例:
const MAX_POINTS: u32 = 100_000;

在这个例子中,MAX_POINTS 是一个常量,类型为 u32,值为 100_000

2. 常量的特点

  • 不可变性:常量的值在定义后不能被修改。如果尝试修改常量的值,编译器会报错。
  • 编译时常量:常量的值必须在编译时确定,不能依赖于运行时的值。
  • 全局可访问性:常量在定义后可以在整个程序范围内访问,不受作用域的限制。

3. 常量的作用域

常量的作用域取决于其定义的位置:

  • 全局常量:如果常量定义在模块的顶层,它可以在整个模块及其子模块中访问。
  • 局部常量:如果常量定义在函数或模块的内部,它只能在该作用域内访问。
示例:
const GLOBAL_CONSTANT: i32 = 42;

fn main() {
    const LOCAL_CONSTANT: i32 = 100;
    println!("Global constant: {}", GLOBAL_CONSTANT);
    println!("Local constant: {}", LOCAL_CONSTANT);
}

在这个例子中,GLOBAL_CONSTANT 是一个全局常量,可以在整个程序中访问;而 LOCAL_CONSTANT 是一个局部常量,只能在 main 函数中访问。

4. 常量的用途

常量在 Rust 中有多种用途,主要包括以下几点:

  • 配置参数:用于定义程序的配置参数,这些参数在运行时不会改变。
  • 数学常量:用于定义数学常量,如 PIE 等。
  • 固定值:用于定义一些固定的值,如数组的大小、枚举的值等。
示例:
const PI: f64 = 3.141592653589793;
const ARRAY_SIZE: usize = 10;

5. 与变量的区别

  • 可变性:变量可以被修改,而常量不能被修改。
  • 存储位置:变量的值存储在内存中,而常量的值通常存储在代码段中。
  • 作用域:变量的作用域通常受限于其定义的位置,而常量的作用域更广泛。

6. 常量的限制

  • 值必须是编译时可知的:常量的值必须在编译时确定,不能依赖于运行时的值。
  • 不能使用函数返回值:不能将函数的返回值赋值给常量,因为函数的返回值是运行时确定的。
示例:
const fn calculate() -> i32 {
    42
}

const VALUE: i32 = calculate(); // 错误:不能在常量定义中调用函数

7. 常量的高级用法

  • 常量泛型:Rust 支持在泛型中使用常量,这可以用于定义数组的大小或其他需要固定值的场景。
  • 常量函数:Rust 允许定义常量函数,这些函数可以在编译时被调用。
示例:
const fn add(a: i32, b: i32) -> i32 {
    a + b
}

const RESULT: i32 = add(2, 3);

常量总结

常量在 Rust 中是一种重要的特性,它用于定义在程序运行期间不可变的值。常量具有不可变性、编译时确定性以及全局可访问性等特点。通过合理使用常量,可以提高代码的可读性和安全性。

三、变量与可变性

1. 变量的声明与初始化

在 Rust 中,变量的声明使用 let 关键字。变量在声明时必须初始化,不能声明一个未初始化的变量。

let x = 5; // 声明一个变量 x,并初始化为 5

2. 默认不可变性

Rust 中的变量默认是不可变的(immutable)。一旦变量被赋值,其值就不能再被改变。这种设计可以防止意外修改变量值,从而减少错误。

let x = 5;
x = 6; // 错误:不能重新赋值给不可变变量

如果需要修改变量的值,必须在声明时明确指定变量为可变的(mutable),使用 mut 关键字。

let mut x = 5; // 声明一个可变变量 x
x = 6; // 正确:可以重新赋值

3. 可变性的作用

可变性(mutability)是 Rust 中一个重要的概念,它允许开发者明确控制变量的修改行为。通过限制变量的可变性,Rust 编译器可以在编译时检查潜在的错误,从而提高代码的安全性和可维护性。

示例:不可变变量
fn main() {
    let x = 5;
    println!("The value of x is: {}", x);

    // x = 6; // 错误:不能重新赋值给不可变变量
    println!("The value of x is: {}", x);
}
示例:可变变量
fn main() {
    let mut x = 5;
    println!("The value of x is: {}", x);

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

4. 变量的绑定与阴影

Rust 允许使用相同的变量名重新声明变量,这种行为称为“阴影”(shadowing)。阴影可以用于在同一个作用域内重新绑定变量,而不会影响原始变量的不可变性。

pub fn f01() {
    let x = 5; // 第一次声明 x
    println!("The value of x is: {}", x);

    let x = x + 1; // 重新声明 x,创建一个新的变量
    println!("The value of x is: {}", x);

    let x = x * 2; // 再次重新声明 x
    println!("The value of x is: {}", x);
}
输出结果:
The value of x is: 5
The value of x is: 6
The value of x is: 12

在这个例子中,虽然变量名都是 x,但每次使用 let 重新声明时,都会创建一个新的变量,而不会改变之前的变量值。

5. 可变性与函数参数

在 Rust 中,函数参数默认也是不可变的。如果需要在函数内部修改参数值,必须显式地将参数声明为可变的。

pub fn f02() {
    let mut x = 5;
    println!("The value of x is: {}", x);

    change(&mut x); // 传递可变引用
    println!("The value of x is: {}", x);
}

fn change(x: &mut i32) {
    *x = 6; // 修改传入的可变引用
}
运行结果:
The value of x is: 5
The value of x is: 6

6. 可变性与并发安全

Rust 的可变性规则在并发编程中尤为重要。通过限制可变性,Rust 编译器可以确保在多线程环境中不会出现数据竞争问题。只有当变量被显式声明为可变时,才能对其进行修改,这使得 Rust 能够在编译时检查并发安全性。

示例:并发中的可变性
use std::thread;

fn main() {
    let mut data = 5;

    let handle = thread::spawn(move || {
        data = 6; // 错误:data 不是可变的
    });

    handle.join().unwrap();
}

为了在多线程中安全地修改变量,可以使用 Arc(原子引用计数)和 Mutex(互斥锁)等工具来管理共享数据。

use std::sync::{Arc, Mutex};
use std::thread;

pub fn f03() {
    let data = Arc::new(Mutex::new(5));

    let data_clone = Arc::clone(&data);
    let handle = thread::spawn(move || {
        let mut num = data_clone.lock().unwrap();
        *num = 6; // 正确:通过互斥锁修改数据
    });

    handle.join().unwrap();

    let num = data.lock().unwrap();
    println!("The value of data is: {}", *num);
}
运行结果:
The value of data is: 6

7. 示例合并

把上面三个示例代码放在不同模块中,使用一个主程序运行所有不同模块中的代码:

mod t01;
mod t02;
mod t03;

fn main() {
    println!("t01::f01()运行结果:");
    t01::f01();
    println!("t02::f02()运行结果:");
    t02::f02();
    println!("t03::f03()运行结果:");
    t03::f03();
}

运行结果

t01::f01()运行结果:
The value of x is: 5
The value of x is: 6
The value of x is: 12
t02::f02()运行结果:
The value of x is: 5
The value of x is: 6
t03::f03()运行结果:
The value of data is: 6

8. 变量与可变性总结

  • 变量默认不可变:Rust 中的变量默认是不可变的,需要使用 mut 关键字显式声明为可变。
  • 阴影机制:可以通过重新声明变量来实现变量的“阴影”,而不会影响原始变量的不可变性。
  • 可变性与函数参数:函数参数默认不可变,需要显式声明为可变。
  • 可变性与并发安全:通过限制可变性,Rust 编译器可以在编译时检查并发安全性,避免数据竞争。

通过合理使用可变性规则,Rust 开发者可以编写出既安全又高效的代码。

总结

本文主要任务是讨论常量、变量及其可变性,根据需要先了解一Rust项目中,使用模块的概念,并按当前内容进行了示例操作,为后面的学习扫清障碍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2374836.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

S7-1500——零基础入门2、PLC的硬件架构

PLC的硬件架构 一,西门子PLC概述二,CPU介绍三,数字量模块介绍四,模拟量模块介绍五,其他模块介绍一,西门子PLC概述 本节主要内容 西门子PLC硬件架构,主要内容包括PLC概述、组成、功能及S7-1500 demo的组成与安装演示。 介绍了PLC的定义、功能、应用场合,以及与继电器控…

【PmHub后端篇】Skywalking:性能监控与分布式追踪的利器

在微服务架构日益普及的当下,对系统的性能监控和分布式追踪显得尤为重要。本文将详细介绍在 PmHub 项目中,如何使用 Skywalking 实现对系统的性能监控和分布式追踪,以及在这过程中的一些关键技术点和实践经验。 1 分布式链路追踪概述 在微服…

利用“Flower”实现联邦机器学习的实战指南

一个很尴尬的现状就是我们用于训练 AI 模型的数据快要用完了。所以我们在大量的使用合成数据! 据估计,目前公开可用的高质量训练标记大约有 40 万亿到 90 万亿个,其中流行的 FineWeb 数据集包含 15 万亿个标记,仅限于英语。 作为…

【RabbitMQ】应用问题、仲裁队列(Raft算法)和HAProxy负载均衡

🔥个人主页: 中草药 🔥专栏:【中间件】企业级中间件剖析 一、幂等性保障 什么是幂等性? 幂等性是指对一个系统进行重复调用(相同参数),无论同一操作执行多少次,这些请求…

软件设计师-错题笔记-系统开发与运行

1. 解析: A:模块是结构图的基本成分之一,用矩形表示 B:调用表示模块之间的调用关系,通过箭头等符号在结构图中体现 C:数据用于表示模块之间的传递的信息,在结构图中会涉及数据的流向等表示 …

C#简易Modbus从站仿真器

C#使用NModbus库,编写从站仿真器,支持Modbus TCP访问,支持多个从站地址和动态启用/停用从站(模拟离线),支持数据变化,可以很方便实现,最终效果如图所示。 项目采用.net framework 4.…

【深度学习】目标检测算法大全

目录 一、R-CNN 1、R-CNN概述 2、R-CNN 模型总体流程 3、核心模块详解 (1)候选框生成(Selective Search) (2)深度特征提取与微调 2.1 特征提取 2.2 网络微调(Fine-tuning) …

视觉-语言-动作模型:概念、进展、应用与挑战(下)

25年5月来自 Cornell 大学、香港科大和希腊 U Peloponnese 的论文“Vision-Language-Action Models: Concepts, Progress, Applications and Challenges”。 视觉-语言-动作 (VLA) 模型标志着人工智能的变革性进步,旨在将感知、自然语言理解和具体动作统一在一个计…

一键解锁嵌入式UI开发——LVGL的“万能配方”

面对碎片化的嵌入式硬件生态,LVGL堪称开发者手中的万能配方。它通过统一API接口屏蔽底层差异,配合丰富的预置控件(如按钮、图表、滑动条)与动态渲染引擎,让工程师无需深入图形学原理,效率提升肉眼可见。 L…

智慧城市综合运营管理系统Axure原型

这款Axure原型的设计理念紧紧围绕城市管理者的需求展开。它旨在打破传统城市管理中信息孤岛的局面,通过统一标准接入各类业务系统,实现城市运营管理信息资源的全面整合与共享。以城市管理者为中心,为其提供一个直观、便捷、高效的协同服务平台…

Qwen智能体qwen_agent与Assistant功能初探

Qwen智能体qwen_agent与Assistant功能初探 一、Qwen智能体框架概述 Qwen(通义千问)智能体框架是阿里云推出的新一代AI智能体开发平台,其核心模块qwen_agent.agent提供了一套完整的智能体构建解决方案。该框架通过模块化设计,将L…

可视化图解算法37:序列化二叉树-II

1. 题目 描述 请实现两个函数,分别用来序列化和反序列化二叉树,不对序列化之后的字符串进行约束,但要求能够根据序列化之后的字符串重新构造出一棵与原二叉树相同的树。 二叉树的序列化(Serialize)是指:把一棵二叉树按照某种遍…

C++GO语言微服务和服务发现②

01 创建go-micro项目-查看生成的 proto文件 02 创建go-micro项目-查看生成的main文件和handler ## 创建 micro 服务 命令:micro new --type srv test66 框架默认自带服务发现:mdns。 使用consul服务发现: 1. 初始consul服务发现&…

【Web前端开发】CSS基础

2.CSS 2.1CSS概念 CSS是一组样式设置的规则,称为层叠样式表,用于控制页面的外观样式。 使用CSS能够对网页中元素位置的排版进行像素控制,实现美化页面的效果,也能够做到页面的样式和结构分离。 2.2基本语法 通常都是&#xff…

Git实战经验分享:深入掌握git commit --amend的进阶技巧

一、工具简介 git commit --amend是Git版本控制系统的核心补救命令,主要用于修正最近一次提交的元数据。该命令不会产生新的提交记录,而是通过覆盖原提交实现版本历史的整洁性,特别适合在本地仓库进行提交优化。 二、核心应用场景 提交信息…

PTA:jmu-ds-最短路径

给定一个有向图&#xff0c;规定源点为0&#xff0c;求源点0到其他顶点最短路径。###你要实现的 函数接口定义&#xff1a; void Dijkstra(MGraph g,int v);//源点v到其他顶点最短路径 裁判测试程序样例&#xff1a; #include <stdio.h> #include <iostream> …

WEB UI自动化测试之Pytest框架学习

文章目录 前言Pytest简介Pytest安装Pytest的常用插件Pytest的命名约束Pytest的运行方式Pytest运行方式与unittest对比主函数运行命令行运行执行结果代码说明 pytest.ini配置文件方式运行&#xff08;推荐&#xff09;使用markers标记测试用例 pytest中添加Fixture&#xff08;测…

深入理解 iOS 开发中的 `use_frameworks!`

在使用 CocoaPods 管理 iOS 项目依赖时&#xff0c;开发者经常会在 Podfile 文件中看到一个配置选项&#xff1a;use_frameworks!。本文将详细介绍这个配置选项的含义&#xff0c;以及如何决定是否在项目中使用它。 一、什么是 use_frameworks! 在 CocoaPods 中引入第三方库时…

矩阵置零算法讲解

矩阵置零算法讲解 一、问题描述 给定一个 (m \times n) 的矩阵,如果一个元素为 (0) ,则将其所在行和列的所有元素都设为 (0) 。要求使用原地算法,即在不使用额外矩阵空间的情况下完成操作。 二、解题思路 暴力解法 最直观的想法是遍历矩阵,当遇到 (0) 元素时,直接将其…

二本计算机,毕业=失业?

我嘞个豆&#xff0c;二本计算机&#xff0c;毕业即失业&#xff1f;&#xff01; 今天咱们聊聊普通院校计算机专业的学生未来的发展方向。有些话可能不太中听&#xff0c;但希望大家能理性看待。 首先得承认&#xff0c;对于普通双非和二本的学生来说&#xff0c;就业率加上…