当前位置: 首页 > article >正文

Rust学习笔记_01——基础

文章目录

    • 1. Hello world
    • 2. 变量
      • 1. 变量声明
      • 2. 类型推断
      • 3. 变量绑定
      • 4. 重绑定(Shadowing)
    • 3. 值
    • 4. 算术
      • 4.1 基础运算
      • 4.2 整数 && 浮点数运算
      • 4.3 标准库函数
        • `std::cmp` 模块
        • `num_traits` 模块
      • 4.4 注意事项
    • 5. 类型推导
      • 5.1 工作原理
      • 5.2 限制
      • 5.3 类型注解的作用


1. Hello world

fn main() {
  println!("hello world!")
    // 函数以 fn 开头。
    // 像 C 和 C++ 一样,块由花括号分隔。
    // main 函数是程序的入口点。
    // Rust 有卫生宏 (hygienic macros),println! 就是一个例子。
    // Rust 字符串是 UTF-8 编码的,可以包含任何 Unicode 字符。
}

在Rust中,宏是一种元编程工具,允许你在编译时生成代码。宏可以用来创建新的语法结构,简化重复的模式,或者实现其他语言特性。Rust的宏系统设计得非常强大且灵活,同时尽量保持安全性和可预测性。

卫生宏是Rust宏的一个重要特性,它确保了宏的使用不会意外地干扰到程序的其他部分。

println!() 是 Rust 中一个非常常用的宏,用于格式化字符串并将其打印到标准输出(通常是控制台)。这个宏是卫生宏的一个很好的例子,因为它展示了宏如何在不影响调用者代码的情况下安全地执行其功能。

println!() 宏的基本用法

println!(fmt, arg1, arg2, ...);
  • fmt 是一个格式字符串,类似于 C 语言中的 printf
  • arg1, arg2, ... 是要插入到格式字符串中的参数。

格式字符串

格式字符串中可以包含占位符,这些占位符会被后面的参数替换。常见的占位符包括:

  • {}:自动选择合适的格式。
  • {:?}:调试格式(Debug trait)。
  • {:<width>}:左对齐,宽度为 width
  • {:>width}:右对齐,宽度为 width
  • {:^width}:居中对齐,宽度为 width
  • {:.precision}:设置浮点数的小数位数或字符串的最大长度。

卫生宏的特点

  1. 作用域隔离
    • println!() 宏内部定义的任何变量或标签都不会与调用者的作用域中的变量或标签冲突。例如,宏内部可能会使用临时变量来构建格式化的字符串,但这些变量不会泄漏到调用者的代码中。
  2. 防止捕获外部变量
    • println!() 宏只会捕获显式传递给它的参数。例如,如果你在格式字符串中使用了 {}, 那么你需要提供相应的参数。宏不会捕获调用者作用域中的其他变量,除非你明确地将它们传递给宏。
  3. 避免名称泄露
    • println!() 宏的内部实现细节对调用者是隐藏的。你不需要关心宏内部的具体实现,只需要知道如何使用它即可。

2. 变量

1. 变量声明

在Rust中,变量默认是不可变的(immutable),这意味着一旦赋值后就不能再修改。要声明一个不可变变量,使用 let 关键字:

let x = 5; // 不可变变量

如果需要声明一个可变变量(mutable),可以在 let 关键字后面加上 mut 关键字:

let mut y = 10; // 可变变量
y = 15; // 修改变量的值

2. 类型推断

Rust具有类型推断功能,编译器可以根据赋值表达式推断出变量的类型。不过,你也可以显式地指定类型:

let z: i32 = 20; // 显式指定类型

3. 变量绑定

变量绑定是指将一个值绑定到一个变量名上。Rust中的变量绑定是不可变的,但可以通过 mut 关键字使其可变:

let a = 1; // 不可变绑定
let mut b = 2; // 可变绑定
b = 3; // 修改可变变量的值

4. 重绑定(Shadowing)

Rust允许在同一作用域内重新声明一个变量,这称为重绑定(shadowing)。重绑定会创建一个新的变量,原来的变量仍然存在,但不可访问:

let x = 5; // 第一次绑定
let x = x + 1; // 重绑定,创建一个新的变量 x
let x = x * 2; // 再次重绑定

println!("The value of x is: {}", x); // 输出 "The value of x is: 12"

3. 值

  • 有符号整数
    • 类型:i8i16i32i64i128isize
    • 字面量:-1001_000123_i64
  • 无符号张数
    • 类型u8u16u32u64u128usize
    • 字面量:12310_u160
  • 浮点数
    • 类型:f32f64
    • 字面量:3.14-10.0e202_f32
  • Unicode标量类型
    • 类型:char
    • 字面量:'a'
  • 布尔值
    • 类型:bool
    • 字面量:truefalse

一些说明

  • iN, uNfN 占用 N 位,
  • isizeusize 占用一个指针大小的空间,
  • char 占用 32 位空间,
  • bool 占用 8 位空间。
  • 数字中的所有下划线均可忽略,它们只是为了方便辨识。因此,1_000 可以写为 1000(或 10_00),而 123_i64 可以写为 123i64

4. 算术

4.1 基础运算

  1. 加法 (+)

    let a = 5;
    let b = 10;
    let sum = a + b; // sum 的值是 15
    
  2. 减法 (-)

    let a = 10;
    let b = 5;
    let difference = a - b; // difference 的值是 5
    
  3. 乘法 (\*)

    let a = 3;
    let b = 4;
    let product = a * b; // product 的值是 12
    
  4. 除法 (/)

    let a = 20;
    let b = 4;
    let quotient = a / b; // quotient 的值是 5
    
  5. 取余 (%)

    let a = 9;
    let b = 4;
    let remainder = a % b; // remainder 的值是 1
    

4.2 整数 && 浮点数运算

整数运算遵循上述基本运算符的规则。需要注意的是,整数除法会舍弃小数部分,例如:

let a = 7;
let b = 3;
let quotient = a / b; // quotient 的值是 2,而不是 2.333...

浮点数运算同样遵循基本运算符的规则,但由于浮点数的表示精度问题,可能会出现一些细微的误差:

let a = 7.0;
let b = 3.0;
let quotient = a / b; // quotient 的值是大约 2.3333333333333335

4.3 标准库函数

Rust标准库提供了一些额外的数学函数,主要位于 std::cmpnum_traits 等模块中。

std::cmp 模块

std::cmp 模块提供了比较函数,例如 maxmin,这些函数可以用于获取两个数值中的最大值或最小值:

use std::cmp;
 
let a = 10;
let b = 20;
let max_value = cmp::max(a, b); // max_value 的值是 20
let min_value = cmp::min(a, b); // min_value 的值是 10
num_traits 模块

num_traits 是一个外部库,提供了丰富的数值运算功能,包括求绝对值、幂运算、平方根等。要使用这个库,需要在 Cargo.toml 文件中添加依赖:

[dependencies]
num-traits = "0.2"

然后在代码中导入并使用相关函数:

use num_traits::{Pow, Abs};
 
let a = -5i32;
let b = 2;
 
let power = a.pow(b); // power 的值是 25,因为 (-5)^2 = 25
let abs_value = a.abs(); // abs_value 的值是 5

4.4 注意事项

  1. 整数溢出:Rust的整数类型在运算时可能会发生溢出。为了避免溢出,可以使用 checked_add, checked_sub, checked_mul, 和 checked_div 等方法,这些方法在溢出时会返回 Option<T> 类型
  2. 浮点数精度:浮点数运算可能会受到精度限制,导致一些细微的误差。

5. 类型推导

Rust中的类型推导(Type Inference)是一种编译器功能,它允许程序员在编写代码时省略变量或表达式的类型注解,而编译器则会自动推断出这些类型。这种功能使得Rust代码更加简洁和易于编写,同时仍然保持了Rust的强类型系统所提供的类型安全性。

5.1 工作原理

Rust编译器通过以下几个步骤来推断类型:

  1. 局部类型推断:编译器首先会查看变量的初始值(如果有的话)来推断其类型。例如,如果有一个变量被初始化为一个整数字面量,那么编译器就会推断这个变量的类型为i32(在大多数情况下,整数字面量的默认类型)。
  2. 上下文依赖:编译器还会考虑变量在表达式中的使用上下文来推断类型。例如,如果一个变量被用作函数参数,并且该函数期望一个特定类型的参数,那么编译器就会推断该变量的类型为该函数参数所期望的类型。
  3. 泛型约束:当使用泛型时,编译器会根据泛型参数的使用方式和提供的类型约束来推断具体的类型。
  4. 类型统一:在推断过程中,编译器会尝试统一不同类型的表达式或变量,以确保它们在使用时具有兼容的类型。如果类型无法统一,编译器就会报错。

5.2 限制

  • 在某些情况下,编译器可能无法推断出变量的类型,这时就需要程序员显式地提供类型注解。
let some_number; // 错误! 无法进行类型推导

let some_number: i32; // OK
let some_number = 123; // OK
  • 当函数返回类型或泛型参数的类型无法从上下文推断出来时,也需要显式地提供类型注解

5.3 类型注解的作用

  1. 提高代码可读性:类型注解可以帮助其他程序员(或未来的你)更容易地理解代码。
  2. 避免潜在的错误:在某些复杂的表达式中,类型推导可能会失败或导致意外的结果。显式地提供类型注解可以避免这些错误。
  3. 与C/C++等语言的互操作性:当与C/C++等语言进行互操作时,可能需要显式地指定类型以确保正确的数据表示和传递。

http://www.kler.cn/a/411286.html

相关文章:

  • RL78/G15 Fast Prototyping Board Arduino IDE 平台开发过程
  • Mongo数据库 --- Mongo Pipeline
  • 如何选择黑白相机和彩色相机
  • Servlet细节
  • 【算法】连通块问题(C/C++)
  • 【AI绘画】Midjourney进阶:色调详解(上)
  • 新能源汽车充电插口类型识别-YOLO标记,可识别Type1,ccs2的充电标准
  • 【机器学习】如何使用Python的Scikit-learn库实现机器学习模型,并对数据进行预处理和特征缩放以提高模型性能?
  • 【青牛科技】D1117 1.0A低压差线性稳压电路芯片介绍,可保证了输出电压精度控制在±1.5%的范围内
  • 【论文复现】融入模糊规则的宽度神经网络结构
  • 【笔记】轻型民用无人驾驶航空器安全操控
  • 51单片机从入门到精通:理论与实践指南入门篇(二)
  • C语言数据结构——详细讲解《栈》
  • 【docker集群应用】Docker常用命令
  • ubuntu 连不上网 桌面 右上角没有网络图标 Ubuntu复制后虚拟网络失效问题
  • Error executing a python function in exec_func_python() autogenerated
  • C++ 二叉搜索树(Binary Search Tree, BST)深度解析与全面指南:从基础概念到高级应用、算法优化及实战案例
  • 基于DVB-T的COFDM+16QAM+LDPC图传通信系统matlab仿真,包括载波同步,定时同步,信道估计
  • Unity中动态生成贴图并保存成png图片实现
  • SAR ADC系列15:基于Vcm-Base的开关切换策略
  • 二叉树的层次遍历
  • web组态可视化编辑器
  • 网上蛋糕售卖店管理系(Java+SpringBoot+MySQL)
  • Web 表单开发全解析:从基础到高级掌握 HTML 表单设计
  • 【真实场景面试问题-2】
  • pwn——test_your_nc1——测试