Rust的一些特性
Rust 相对于其他编程语言的一些特性,这里和C++比较
变量与可变性
不同于其他编程语言,Rust的变量默认是不可变的(这句话第一次听很别扭,可以想象成Rust的变量默认是C++的const(不太恰当的类别)),若想要定义可变变量则使用mut关键字
let x = 1;//x不可变
let mut x1 = 1;//x1可变
变量遮蔽
Rust允许定义同名变量,这样会产出变量遮蔽(即之间的变量被新定义的变量遮蔽而不可用)
let x = 1;
let x = 'A';//产生变量遮蔽
//C++中这样的语法是报错的
语句和表达式
Rust是一门基于表达式的语言,所以相较于其他语言,Rust很重视二者的区别:
- 语句(Statements)是执行一些操作但不返回值的指令。
- 表达式(Expressions)计算并产生一个值。
fn f() -> i32 {
let x = 520;//语句
return x;//语句
}
fn f() -> i32 {
520//表达式
}
上述两个函数均可正常编译通过。
注释
基本注释语法与C++相同,除此之外Rust引入了文档注释(可以生成HTML文档,便于生成和展示项目文档)。
//单行注释
/*
多行注释
*/
///文档注释
所有权
Rust 的核心功能(之一)是 所有权(ownership)。下面是所有权规则:
- Rust 中的每一个值都有一个 所有者(owner)。
- 值在任一时刻有且只有一个所有者。
- 当所有者(变量)离开作用域,这个值将被丢弃。
作用域概念和C++基本一致,所有权的概念就类似C++的RAII机制,从语法层面就实现了对资源的管理。
Rust中变量和数据的交互形式(和C++几乎一致):
- 移动(move):堆上的数据(例如String,编译时大小未知)
- 克隆(clone):堆上的数据(例如String,编译时大小未知)
- 拷贝(copy):只在栈上的数据(例如i32,编译时大小已知)
//例子1 栈上变量发生copy,copy后二者均可用
let x = 5;
let y = x;
//例子2 堆上变量发生move,move后s1所有权转移给s2,s1不可用
let s1 = String::from("hello");
let s2 = s1;
//例子2 堆上变量发生clone,clone后s1依然可用
let s1 = String::from("hello");
let s2 = s1.clone();
Rust的引用与C++几乎一致,特别的存在以下几个规则:
- 在任意给定时间,要么只能有一个可变引用,要么只能有多个不可变引用。
- 引用必须总是有效的。
Option
Rust没有null类型,其使用Option来解决值为空时的情况.
//Option定义在标准库中
enum Option<T> {
None,
Some(T),
}
//简单使用
fn main() {
let x: i8 = 5;
let y: Option<i8> = Some(5);
let z = x + y;//编译会产生错误
}
当有一个 Some 值时,我们就知道存在一个值,而这个值保存在 Some 中。当有个 None 值时,在某种 意义上,它跟空值具有相同的意义:并没有一个有效的值。
Option 为什么就比空值要好呢?
简而言之,因为 Option 和 T(这里 T 可以是任何类型)是不同的类型,编译器不允许像一个肯定有效的值那样使用 Option。换句话说,在对 Option 进行 T 的运算之前必须将其转换为 T。通常这能帮助我们捕获到空值最常见的问题之一:假设某值不为空但实际上为空的情况。从而避免了某变量为空带来的运行时错误,这也是Rust安全的一个体现。
模式和模式匹配
模式是 Rust 中特殊的语法,它用来匹配类型中的结构,无论类型是简单还是复杂。结合使用模式和 match 表达式以及其他结构可以提供更多对程序控制流的支配权。
可能用到模式的地方:
//1.match分支 match必须是穷尽的
match VALUE {
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
}
//2.if let 主要用于编写等同于只关心一个情况的 match 语句简写
//else是可选的
if let Some(color) = favorite_color {
println!("Using your favorite color, {}, as the background", color);
} else{
println!("Tuesday is green day!");
}
//3.while let
while let Some(top) = stack.pop() {
println!("{}", top);
}
//4.for
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{} is at index {}", value, index);
}
//5.let
let PATTERN = EXPRESSION;
模式的可反驳性:
模式有两种形式:refutable(可反驳的)和 irrefutable(不可反驳的)。能匹配任何传递的可能值的模式 被称为是 不可反驳的(irrefutable)。一个例子就是 let x = 5; 语句中的 x,因为 x 可以匹配任何值所以 不可能会失败。对某些可能的值进行匹配会失败的模式被称为是 可反驳的(refutable)。一个这样的例子 便是 if let Some(x) = a_value 表达式中的 Some(x);如果变量 a_value 中的值是 None 而不是 Some, 那么 Some(x) 模式不能匹配。
泛型
Rust具有泛型,和C++泛型几乎是类似的
trait
trait 告诉 Rust 编译器某个特定类型拥有可能与其他类型共享的功能。可以通过 trait 以一种抽象的方式 定义共享的行为。类似于其他语言中的接口。
一个类型的行为由其可供调用的方法构成。如果可以对不同类型调用相同的方法的话,这些类型就可以 共享相同的行为了。trait 定义是一种将方法签名组合起来的方法,目的是定义一个实现某些目的所必需 的行为的集合。
//一个简单的例子
//定义一个trait
pub trait Summary {
fn summarize(&self) -> String;
//trait默认实现
fn summarize1(&self) -> String {
String::from("(Read more...)")
}
}
//结构体
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
//该结构体实现Summarytrait
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
//使用trait作为参数(类比相当于C++的虚基类。),
//任何实现了Summary trait的类型都是合法参数
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
//trait bound语法 (类比相当于c++的concept)
//语法示例1
pub fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
//语法示例2,泛型 T 被指定为 item1 和 item2 的参数限制,如此传递给参数 item1 和 item2 值的具体类型必须一致。
pub fn notify<T: Summary>(item1: &T, item2: &T) {}
//还有很多,这里只是抛砖引玉
生命周期
Rust的每一个变量都有其生命周期,基本上和C++类似。特别的是Rust中的生命周期注解语法。
**生命周期注解并不改变任何引用的生命周期的长短。**与当函数签名中指定了泛型类型参数后就可以接受 任何类型一样,当指定了泛型生命周期后函数也能接受任何生命周期的引用。生命周期注解描述了多个 引用生命周期相互的关系,而不影响其生命周期。
生命周期注解有着一个不太常见的语法:生命周期参数名称必须以撇号(’)开头,其名称通常全是小写, 类似于泛型其名称非常短。
&i32 // 引用
&'a i32 // 带有显式生命周期的引用
&'a mut i32 // 带有显式生命周期的可变引用
单个的生命周期注解本身没有多少意义,因为生命周期注解告诉 Rust 多个引用的泛型生命周期参数如 何相互联系的。例如如果函数有一个生命周期 ’ a 的 i32 的引用的参数 first 。还有另一个同样是生命周 期 ’ a 的 i32 的引用的参数 second。这两个生命周期注解意味着引用 first 和 second 必须与这泛型生 命周期存在得一样久。
这部分和trait是Rust比较难的部分,这里只是简要概括
闭包
Rust 的 闭包(closures)是可以保存在一个变量中或作为参数传递给其他函数的匿名函数。可以在一个 地方创建闭包,然后在不同的上下文中执行闭包运算。不同于函数,闭包允许捕获被定义时所在作用域中的值。(类比相当于C++中的lambda表达式)
语法比较奇特,集体不详细解释,这应该当成专题学习,这是Rust支持函数式编程的一个重要特性。
个人总结
感觉基础的Rust相较于C++比较不同的地方就是上述内容啦,当然 他还有很多自己高级的语法和特性,有待挖掘中…