Rust快速入门(二)

news/2024/12/12 15:58:16/

三个指令:

  • cargo run 执行

    • --release:

      由于使用run命令rust默认为debug模式,代码中很多debug数据就会打印,于是我们使用relsase参数就可以不输出debug的代码。

  • cargo check 校验是否能够通过编译

  • cargo build 打包为可执行文件

Cargo.toml & Cargo.lock

Cargo.toml:

是cargo特有的项目数据描述文件,存储了项目所有元配置信息。

Cargo.lock

是cargo工具根据同一项目的toml文件生成的项目依赖详细清单

所以一般情况下我们只需要修改Cargo.toml即可(这里可以联想到golang的mod和sum包管理文件)

所以当项目为一个可运行项目时,就上传Cargo.lock,如果是一个依赖库项目,那么就把它添加到 .gitignore 中。

项目依赖定义

Cargo.toml 中,主要通过各种依赖段落来描述该项目的各种依赖项:

  • 基于Rust官方仓库 crates.io,通过版本说明来描述

  • 基于项目源码的git仓库来描述

  • 基于本地项目的绝对路径或者相对路径,通过类 Unix 模式的路径来描述

为什么手动设置变量可变性

既要安全性,又要灵活性。一切选择皆是权衡。这样一来也可以减少runtime过程中多余的检查。

变量绑定

举个栗子:

rust">let a = "hello"

这里就是把 "hello" 绑定到了 a。为什么不是赋值而是绑定呢?这里就要涉及到rust的核心原则——所有权,即任何内存对象都有主人,如果将 "hello" 绑定给 a 那么之前的主人就失去了 "hello" 所有权。

如果我们希望该变量可变那么我们需要作如下操作:

rust">let mut a = "hello"

这样一来,我们就可以对a进行多次赋值。

当我们希望提前定义一些变量

使用下划线开头忽略未使用的变量。因为如果声明了变量却不使用,rust就会报warning,所以我们需要作如下操作:

rust">let _a = "haha"

变量遮蔽

rust中,允许声明相同的变量名,在后面的声明的变量会遮蔽掉前面声明的。

这样做的好处是:如果你在某个作用域内无需再使用之前的变量(在被遮蔽后,无法再访问到之前的同名变量),就可以重复的使用变量名字,而不用绞尽脑汁去想更多的名字。

基础类型

Rust分为两类:基本类型和复合类型

基本类型如下:

  • 数值类型:有符号整数(i8, i16,i32, i64, isize)、无符号整数(u8, u16, u32, u64, usize) 、浮点数 (f32, f64)、以及有理数、复数

  • 字符串:字符串字面量和字符串切片 &str

  • 布尔类型:truefalse

  • 字符类型:表示单个 Unicode 字符,存储为 4 个字节

  • 单元类型:即 () ,其唯一的值也是 ()

注意:浮点类型虽然可以比较,但是在rust中 0.1+0.2 != 0.3 我们需要注意,当类型为f32相加时不会报错,但是如果为f64就会导致程序崩溃。

语句和表达式

在Rust中函数体是由一系列语句(statement)组成,最后由一个表达式(expression)来返回值:

rust">fn main() {let res = add_with_extra(1, 1);println!("the result is {}", res)
}
​
fn add_with_extra (x:i32, y:i32) -> i32 {let x = x+1;let y = y+5;x+y
}

我们需要注意的是区别rust与其他语言,基于表达式是函数式语言的重要特征表达式总要返回值。

  • 语句:

    完成了一个具体的操作,但是并没有返回值,如:

    rust">let a = 8;
    let b: Vec<f64> = Vec::new();
    let (a, c) = ("hello", 1);

    由于let是语句,不是表达式,所以不能将let语句赋值给其他值

  • 表达式:

    表达式会进行求值,例如:

    rust">5+6
    let y = 6 // 6是一个表达式,求值后返回一个6

    注意:表达式后面不能跟分号(;),如果跟了分号表达式就不返回值了

    调用一个函数是表达式,因为会返回一个值,调用宏也是表达式,用花括号包裹最终返回一个值的语句块也是表达式。

函数

举例:

rust">fn add(i:i32, j:i32) -> i32 {i+j
}

也可以直接放在main函数中,如:

rust">fn main() {let res = add_with_extra(1, 1);println!("the result is {}", res);
​fn add(i:i32, j:i32) -> i32 {i+j}let res1 = add(1, 2);println!("the result1 is {}", res1);
}

所以综上,我们给出三条函数结论:

  • 函数名和变量名使用蛇形命名法(snake case),例如 fn add_two() -> {}

  • 函数的位置可以随便放,Rust 不关心我们在哪里定义了函数,只要有定义即可

  • 每个函数参数都需要标注类型(Rust 是静态类型语言,因此需要你为每一个函数参数都标识出它的具体类型)

特殊返回类型

  • 无返回值()

    单元类型()是一个零长度的元组,可以用于表示一个函数没有返回值:

    • 函数没有返回值,返回一个()

    • 通过 ; 结尾的语句返回一个()

    举例:

    rust">fn report<T: Debug>(item: T) {println!("{:?}", item);
    ​
    }
    ​
    //或
    ​
    fn clear(text: &mut String) -> () {*text = String::from("");
    }

  • 永不返回的发散函数

    当用 ! 作函数返回类型的时候,表示该函数永不返回( diverge function ),特别的,这种语法往往用做会导致程序崩溃的函数:

    rust">fn dead_end() -> ! {panic!("你已经到了穷途末路,崩溃吧!");
    }

所有权

在内存管理中,程序如何从内存空间申请内存,在不需要的时候如何安全释放内存是十分重要的部分,目前存在三种流派:

  1. 垃圾回收机制(GC):在运行时不断寻找不再使用的内存,如 Golang,Java。

  2. 手动管理内存分配与释放:在程序运行过程中,通过函数调用的方式来申请和释放内存,如Cpp。

  3. 通过所有权来管理内存:编译器在编译时会进行一系列检查。

所有权原则

  • Rust中每一个值都被一个变量所拥有,改变量称为值的所有者

  • 一个值同时只能被一个变量所拥有(一个值只能有一个所有者)

  • 当所有者(变量)离开作用范围时,这个值会被丢弃

举例说明:

rust">let x = 1;
let y = x;

在上述语句中,由于int为基础类型在栈上分配,所以拷贝会快于所有权移动,所以如果打印输出的话,上述x,y都为1,因为整数是 Rust 基本数据类型,是固定大小的简单值,因此这两个值都是通过自动拷贝的方式来赋值的。

然而在下述代码中:

rust">let s1 = String::from("hello");
let s2 = s1;

则需要注意了,因为String是复合类型,所以s1 被赋予 s2 后,Rust 认为 s1 不再有效,因此也无需在 s1 离开作用域后 drop 任何东西,这就是把所有权从 s1 转移给了 s2s1 在被赋予 s2 后就马上失效了

和浅拷贝很类似,但是为了避免二次释放前者失去了指针,所以我们称之为移动

Rust 永远也不会自动创建数据的 “深拷贝”

Copy特征

Rust 有一个叫做 Copy 的特征,可以用在类似整型这样在栈中存储的类型。如果一个类型拥有 Copy 特征,一个旧的变量在被赋值给其他变量后仍然可用,也就是赋值的过程即是拷贝的过程。

那么什么类型是可 Copy 的呢?可以查看给定类型的文档来确认,这里可以给出一个通用的规则: 任何基本类型的组合可以 Copy ,不需要分配内存或某种形式资源的类型是可以 Copy。如下是一些 Copy 的类型:

  • 所有整数类型,比如 u32

  • 布尔类型,bool,它的值是 truefalse

  • 所有浮点数类型,比如 f64

  • 字符类型,char

  • 元组,当且仅当其包含的类型也都是 Copy 的时候。比如,(i32, i32)Copy 的,但 (i32, String) 就不是

  • 不可变引用 &T但是可变引用 &mut T 是不可以 Copy的

引用与借用

如果仅仅通过转移所有权的方式获取一个值,程序将会变得非常复杂。于是Rust采用了 借用(Borrowing) 这个概念来减少复杂度。

引用与解引用

常规引用是一个指针类型,指向了对象存储的内存地址,如:

rust">let x = 5;
let y = &x;
​
assert_eq!(5,x);
assert_eq!(5,*y);

不可变引用

举例说明:

rust">fn main() {let s1 = String::from("hello");
​let len = calculate_length(&s1);
​println!("The length of '{}' is {}.", s1, len);
}
​
fn calculate_length(s: &String) -> usize {s.len()
}

这里 s1 所有权传给了 calculate_length() 函数,但是我们无法修改s1。

可变引用

我们可以通过修改s1为可变变量,calculate_length() 接收可变引用来达到我们希望修改s1的目的。

rust">fn main() {let mut s = String::from("hello");
​change(&mut s);
}
​
fn change(some_string: &mut String) {some_string.push_str(", world");
}

但是!同一作用域,特定数据只能有一个可变引用!这样做使Rust在编译期间就避免了数据竞争。

导致数据竞争的行为:

  • 两个或更多的指针同时访问同一数据

  • 至少有一个指针被用来写入数据

  • 没有同步数据访问的机制

可变引用和不可变引用不可以同时存在

  • 这里就和读写锁很类似,不希望造成脏读。

  • 在新编译器中,引用作用域的结束位置从花括号变成了最后一次使用的位置。

  • 对于上述这种编译器优化行为(找到某个引用在作用域(})结束前就不再被使用的代码位置),Rust称其为——Non-Lexical Lifetimes(NLL)

悬垂引用

也叫做悬垂指针,意为指针指向某个值后,这个值被释放掉了,而指针仍然存在,其指向内存可能不存在或者已被其他变量重新引用。

rust中,编译器可以确保引用永远也不会变成悬垂状态:当你获取数据引用后,编译器可以确保数据不会在引用结束前被释放,想要释放数据,必须先停止其引用的使用。

所以总结一下,借用规则如下:

  • 同一时刻,你只能拥有要么一个可变引用,要么任意多个不可变引用

  • 引用必须总是有效的


http://www.ppmy.cn/news/1554537.html

相关文章

【jvm】GC Roots有哪些

目录 1. 说明2. 虚拟机栈&#xff08;栈帧中的局部变量表&#xff09;中的引用3. 方法区中的类静态属性引用4. 本地方法栈&#xff08;Native方法栈&#xff09;中JNI&#xff08;Java Native Interface&#xff09;的引用5. 活跃线程&#xff08;Active Threads&#xff09;6.…

Scala的正则表达式(1)

package hfd //正则表达式的应用场景 //1.查找 findAllin //2.验证 matches //3.替换//验证用户名十分合法 //规则&#xff1a; //1.长度在6-12之间 //2.不能数字开头 //3.只能包含数字&#xff0c;大小写字母&#xff0c;下划线 object Test36 {def main(args: Array[String])…

ubuntu 18.04 server常用配置

1. 配置静态ip 编辑/etc/netplan目录下的配置文件。以下是步骤和示例配置&#xff1a; 找到Netplan配置文件&#xff0c;通常命名为01-netcfg.yaml&#xff0c;50-cloud-init.yaml或类似。 使用文本编辑器编辑该文件&#xff0c;比如使用nano或vim。 修改配置文件以设置静态…

top命令和系统负载

1 top中的字段说明 top是一个实时系统监视工具&#xff0c;可以动态展现出 CPU 使用率、内存使用情况、进程状态等信息&#xff0c;注意这些显示的文本不能直接使用 > 追加到文件中。 [rootvv~]# top -bn 1 | head top - 20:08:28 up 138 days, 10:29, 4 users, load av…

后端处理跨域问题

1. CORS&#xff08;跨域资源共享&#xff09; 操作步骤&#xff1a; 设置CORS头部&#xff1a; 对于简单的GET、POST或HEAD请求&#xff0c;服务端需要在HTTP响应中添加Access-Control-Allow-Origin头部&#xff0c;以指定允许访问的源。例如&#xff0c;在Node.js中使用Expr…

【机器人】控制之稳定性判定: 李雅普诺夫Lyapunov (2) 如何设计李(李雅普诺夫)函数

系统模型和构造 Lyapunov 函数之间是有关系的&#xff0c;但这种关系并不是唯一的&#xff0c;也就是说&#xff0c;构造 Lyapunov 函数需要参考系统模型的特性&#xff0c;但可以有多种选择。以下从理论和实践两方面解释它们的关系。 理论上的关系 系统模型给出动态行为&#…

Spring Boot 集成 MyBatis 全面讲解

Spring Boot 集成 MyBatis 全面讲解 MyBatis 是一款优秀的持久层框架&#xff0c;与 Spring Boot 集成后可以大大简化开发流程。本文将全面讲解如何在 Spring Boot 中集成 MyBatis&#xff0c;包括环境配置、基础操作、高级功能和最佳实践。 一、MyBatis 简介 1. SqlSession …

Github 2024-12-08 php开源项目日报 Top10

根据Github Trendings的统计,今日(2024-12-08统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目10Shell项目1JavaScript项目1Blade项目1Laravel:表达力和优雅的 Web 应用程序框架 创建周期:4631 天开发语言:PHP, BladeStar数量:7…