rust基础1
cargo 常用命令
cargo new objname 创建项目
cargo run 运行项目
cargo install --force --path . 安装 .目录下的项目,将编译好的二进制文件拷贝到~/.cargo/bin中
变量绑定
Rust 中,使用关键字 let 声明变量。
let x = 5;
在 Rust 中,变量默认是不可改变的(immutable),当变量不可变时,一旦值被绑定到一个名称上,这个值就不能被改变,这样的设计有助于防止意外的数据修改和并发问题。若
要使得变量可变,需要在声明时使用 mut 关键字。
let mut x = 5;
x = 10;
常量
Rust 中常量总是不可变,声明常量使用 const 关键字而不是 let。
常量只能被声明为常量表达式,而不可以是其他任何只能在程序运行时计算出的值。
常量定义时候需要指定类型
const xx:i32 = 15;
标量类型
• 有符号整数(signed integers):i8、i16、i32、i64、i128 和 isize(指针宽度)
• 无符号整数(unsigned integers): u8、u16、u32、u64、u128 和 usize(指针宽度)
• 浮点数(floating point): f32、f64
• char(字符):单个 Unicode 字符,如 ‘a’,’α’ 和 ‘∞’(每个都是 4 字节)
• bool(布尔型):只能是 true 或 false
• 单元类型(unit type):()。其唯一可能的值就是 () 这个空元组
复合类型
数组
数组是一个线性的集合,每个数据元素的类型都是相同的
数组的类型定义为 [元素类型;元素数量] 在初始化时可以对每个元素都做一遍初始化[1,2,3,4,5],也可以用[值;数量]来做批量的初始化
let array:[i32;5] = [5;5];
数组的操作
array[0] //访问元素
array[0] = 1; //写数据 注意必须是通过mut关键字创建的数组才支持写数据
元组
元组是一种可以将多种不同类型的数据元素绑定到一个集合的数据类型
通过tup.0 tup.1 点+成员下标来访存
let mut tup:(i32,i32,i32) = (1,2,3);
tup.0 = 10;
println!("is {}",tup.0);
使用模式匹配解构元组
let mut tup:(i32,i32,i32) = (1,2,3); // 元组的定义
tup.0 = 10; // 访问 读写元组的元素,通过 元组名.idx去访问
println!("is {}",tup.0);
let (a,b,c) = tup; //模式匹配可以解构元组的值,将元组的值分配到几个变量上
函数定义
fn function_name() -> return_tpye{
do_someting
}
if表达式
if后面的条件表达式不需要写括号
if a > b {
}
if是一个表达式还可以有返回值 用于变量绑定语句
let if_a = if 1> 0 {100} else {10};
println!("{}",if_a);
loop循环
loop 关键字会一遍又一遍地执行一段代码直到break,等价于 while
可以通过continue跳过单次循环,可以通过break退出循环
let mut i = 100;
loop {
println!("{}",i);
i = i - 1;
if i < 1 {
break;
}
}
loop表达式有返回值就像if一样
let mut i = 100;
let s = loop {
println!("{}",i);
i = i - 1;
if i < 1 {
break 100;
}
};
println!("{}",s);
循环标签
loop可以存在循环标签,可以通过break和continue跳转到某个标签中,一般用于多层loop的情况
'loop1 : loop {
println!("loop1");
'loop2 : loop {
println!("loop2");
break 'loop1;
}
}
在loop关键字前使用 ‘循环标签名字 : 这样的表达式就可以给循环打标签,在break中需要 ‘循环标签名来实现跳转到某个loop循环标签中
while循环
let mut i = 0;
while i < 10 {
i = i + 1;
println!("i is{}",i)
}
while循环循环条件那没有括号,没有返回值0
for循环
for循环似乎主要用于遍历迭代器,实现了可迭代trait的对象就可以用for去迭代?
for i in 1..5{
println!("{}",i);
}
迭代数组
let test_array:[i32;10] = [2;10];
for i in test_array {
println!("{}",i);
}
所有权(ownership)
定义:
Rust 中的每一个值都有一个所有者(owner)
值在任一时刻有且只有一个所有者
当所有者(变量)离开作用域,这个值将被丢弃
如果某个对象实现了copy trait 在进行变量绑定操作时候会直接复制对象的值过去,而不是会转移所有权
所有权机制就是为了保证在某一段时间内,某个值只有一个所有者,为了避免数据竞争
这个表达式的行为是 将123绑定到符号x,然后再次绑定到y中,由于值在任意时刻只有一个所有者,x会被丢弃
let x = 123;
let y = x;
变量隐藏
这个样例中 第一个变量被第二个 隐藏(Shadowing) 了
fn main() {
let x = 5;
let x = x + 1;
{
let x = x * 2;
println!("The value of x in the
inner scope is: {x}");
}
println!("The value of x is: {x}");
}
函数传参会转移所有权
s作为参数传递到 test函数时,所有权被转移到test中
fn test(a:String) -> String {
return a;
}
fn main() {
let s = String::from("hello");
test(s);
println!("{}",s);
}
函数返回值也会转移所有权
fn test(a:String) -> String {
return a;
}
fn main() {
let s = String::from("hello");
let s = test(s);
println!("{}",s);
}
引用和借用
引用(reference)像一个指针,因为它是一个地址,我们可以由此访问储存于该地址的属于其他变量的数据。 与指针不同,引用确保指向某个特定类型的有效值,我们将创建一个引用的行为称为 借用(borrowing)。
传递引用并不会转移所有权
fn test(a:&String){
println!("{}",a);
}
fn main() {
let s = String::from("hello");
test(&s);
println!("{}",s);
}
可变引用
可变引用可以在引用的基础上传递一个可以读写数据的”指针”,而不转移数据的所有权
同一作用域下可以存在多个对于同一对象的不可变引用,但是不能存在多个对于同一对象可变引用,不然可能会产生数据竞争,也不能同时存在对于同一对象的可变引用和不可变引用
fn test(a:&mut String){
a.push_str("AAAAAAA");
println!("{}",a);
}
fn main() {
let mut s = String::from("hello");
test(&mut s);
println!("{}",s);
}
String类型
String类型支持两种创建方式
通过String::from创建 通过”aaa”.to_string() 字符串字面量类型转换创建
fn main() {
let s1 = String::from("tests1");
let s2 = "tests2".to_string();
println!("{}",s1);
println!("{}",s2);
}
slice类型
slice可以创建一个针对某个集合的一段不可变引用,它并没有获取这段数据的所有权
fn main() {
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];
let str = String::from("test_str1");
let str2 = &str[0..3];
println!("{}",str2);
}
struct结构体
struct的创建方式是 struct 结构体名然后在花括号里面定义结构体有的字段
struct test_struct {
a: i32
b: i64
}
结构体实例创建,访问字段
struct User{
Username:String,
Password:String
}
fn main() {
let mut User1 = User{
Username : "User1".to_string(),
Password : "123456".to_string()
};
User1.Username = String::from("User2");
println!("{}",User1.Username);
}
元组结构体
元组结构体也是一种能将不同数据绑定在一起的一个复合类型,和结构体的区别是元组结构体的成员没有一个具体的字段名
元组结构体的创建 struct 元组结构体名 然后在圆括号中定义数据成员
struct Color(i32,i32,i32);
fn main() {
let mut black = Color(255,255,255);
println!("{}",black.0);
black.0 = 0;
println!("{}",black.0);
}
枚举类型
枚举类型用于存放一些同属组,但是互斥的状态,枚举类型中的每个子类型可以和一些数据类型做关联,比如说和元组关联,和结构体关联
枚举定义
enum IpAddrKind {
V4,
V6,
}
// 包含成员关联类型的定义
enum IpAddr {
V4(String),
V6(String),
}
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
枚举实例
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
let messages = [
Message::Move { x: 10, y: 30 },
Message::Echo(String::from("hello world")),
Message::ChangeColor(200, 255, 255),
Message::Quit,
];
枚举定义方法
impl Message {
fn call(&self) {
// 方法体 dosomethink
}
}
Option枚举
在rust的设计哲学中 似乎通过类型系统来表示某个东西是否有值?option
enum Option<T> {
None,
Some(T),
}
Option枚举中的一些常用的方法
v.expect(msg); // 如果Option是Some,返回里面的值,否则panic并显示msg
v.unwrap_or(default); // 如果Option是Some,返回里面的值,否则返回default
v.unwrap_or_else(f); // 如果Option是Some,返回里面的值,否则调用函数
v.map(f); // 如果Option是Some,调用函数f并返回新的Option,否则返回
v.and_then(f); // 如果Option是Some,调用函数f并返回新的Option,否则返回None
v.is_some(); // 如果Option是Some,返回true,否则返回false
v.is_none(); // 如果Option是None,返回true,否则返回false
Result<T,E>枚举
Result<T, E> 可以理解为“要么成功返回一
个 T 类型的值,要么失败返回一个 E 类型的
错误”的类型。
enum Result<T, E> {
Ok(T),
Err(E),
}
result枚举中的一些常用方法
v.expect(msg); // 如果Result是Ok,返回里面的值,否则panic并显示msg
v.unwrap_or(default); // 如果Result是Ok,返回里面的值,否则返回default
v.unwrap_or_else(f); // 如果Result是Ok,返回里面的值,否则调用函数
v.map(f); // 如果Result是Ok,调用函数f并返回新的Result,否则返回Err
v.and_then(f); // 如果Result是Ok,调用函数f并返回新的Result,否则
v.or_else(f); // 如果Result是Ok,返回里面的值,否则调用函数f并返回新的Result
v.is_ok(); // 如果Result是Ok,返回true,否则返回false
v.is_err(); // 如果Result是Err,返回true,否则返回false
?操作符
? 操作符用于简化错误处理的代码,是一个语法糖,这个操作符用于对函数的返回值进行进行解构,如果是OK或者Some表达式会返回枚举类型里的值,如果是Err或者None的话就会返回Err或者None,实现了std::ops::Try trait类型才可以使用?操作符进行解构,
e.g.
fn find_item(name: &str) -> Option<String> {
if name == "apple" {
Some("Apple".to_string())
} else {
None
}
}
fn process_item(name: &str) -> Option<String> {
let item = find_item(name)?;
Some(item.to_uppercase())
}
fn main() {
let result = process_item("apple");
println!("{:?}", result);
let result2 = process_item("banana");
println!("{:?}", result2);
}
模式化匹配
模式化匹配是一种语法结构,用于解构复合数据结构(元组 结构体 元组结构体 枚举)
其用法其实就是在变量绑定表达式的左表达式中写需要结构的复合类型的类型初始化表达式,然后用代数符号填入原本初始化表达式需要填值的位置。
e.g.
struct Point {
x:i32,
y:i32
}
struct RGB (i32,i32,i32);
fn main() {
let tup = (1,2,3);
let (a,b,c) = tup; //模式化匹配解构元组
let point = Point{
x:10,
y:20
};
let Point{x:a,y:b} = point; //模式化匹配解构结构体
let white = RGB(255,255,255);
let RGB(a,b,c) = white; //模式化匹配解构元组结构体
let value = Some(1); // 模式化匹配解构枚举
let Some(a) = value else {
println!("value is none");
return;
};
if let Some(a) = value {
println!("value is {}",a);
}
else {
println!("value is none");
}
}
if let …. else 相对 let …. else的区别
if let … else 相对 let …. else语法块的区别就是 if let … else可以在模式化匹配成功后执行一个代码块,匹配失败后执行一个代码块,let … else只能在匹配失败执行一个代码块
ref
ref关键字是用于模式化匹配的一个关键字,匹配成功后得到的结果是对某个东西的引用,可以在不转移所有权的情况下对某个东西进行解构
fn main() {
let x = 5;
let ref y = x;
let some = Some(String::from("hello"));
let Some(ref value) = some else {return;};
println!("{}",value);
}
match
match是有返回值的
match类似于switch,主要用于枚举类型的模式匹配,
对于不需要使用的值,可以使用 _ 通配符来忽略
fn main() {
let number = Some(5);
match number {
Some(n) => println!("{}", n),
_ => println!("None"),
}
}
while let
while let 语句用于在循环中进行模式匹配,类似于 if let 语句。
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("{}", top);
}
泛型
泛型是指在编程中可以在定义函数 结构体 枚举 方法时,指定模糊的类型,然后在实际使用的时候再指定具体的类型
泛型函数的定义,在函数名后面加一个<>方括号表示这是一个泛型函数,方括号内的内容表示这个函数可能用到的 模糊的类型T, 在T类型: 后面的则是一个trait,trait是一个对泛型的某些特性或者行为进行约束的约束,用于确保传过来的类型必须具备某些特定的行为,比如说是可比较的行为,是可复制的行为等等
fn max<T: PartialOrd>(a: T, b: T) -> T {
if a > b { a } else { b }
}
trait
这两种写法完全是等价的
fn max<T: PartialOrd>(a: T, b: T) -> T {
if a > b { a } else { b }
}
fn max<T>(a: T, b: T) -> T
where T: PartialOrd
{
if a > b { a } else { b }
}
可以使用 + 来表示多个trait约束
fn max<T: PartialOrd + Copy>(a: T, b: T) -> T {
if a > b { a } else { b }
}
impl语法糖使用泛型
fn pr(s: impl Display) {
println!("{}",s);
}
fn main() {
pr("ciallo");
pr(1);
}
泛型结构体
struct Point<T>{
x:T,
y:T
}
fn main() {
let test1 = Point{x:1,y:2};
let test2 = Point{x:1.1,y:1.1};
}
泛型枚举
enum Option<T> {
None,
Some(T)
}
const泛型
struct ArrayWrapper<T, const N: usize> {
data: [T; N],
}
let arr = ArrayWrapper { data: [1, 2, 3] };
trait定义
trait MyTrait {
const a:i32 = 4;
fn do_somethink(&self);
}
为某种类型实现trait
struct MyStruct;
trait MyTrait {
fn do_something(&self){
}
}
impl MyTrait for MyStruct {
fn do_something(&self) {
println!("Doing something!");
}
}
fn main(){
MyStruct.do_something();
}
trait_bound
当MyStruct
类似于cpp中的偏特化,给某个支持泛型参数的结构体或者枚举或者函数的泛型参数做偏特化,只有泛型参数实现了MyTrait才能使用call_do_something方法
impl<T: MyTrait> MyStruct<T> {
fn call_do_something(&self) {
self.value.do_something();
}
}
trait继承
trait可以继承一个trait,对于任何实现了SummaryExt的类型必须得先实现Summary
trait Summary {
fn summarize(&self) -> String;
}
trait SummaryExt: Summary {
fn summarize_with_prefix(&self, prefix: &str) -> String {
format!("{}: {}", prefix, self.summarize())
}
}
常见的trait
• Drop:析构函数,在值离开作用域时调用。
• Copy:表示类型的值可以按位复制,在用 let 绑定时不会移动所有权。
• Clone:表示类型的值可以显式复制。
• Default:表示类型有一个默认值。
• Debug:表示类型可以格式化输出,通常用于调试。
• Display:表示类型可以格式化输出,通常用于用户界面。
• PartialEq 和 Eq:表示类型可以进行相等性比较。
• PartialOrd 和 Ord:表示类型可以进行排序比较。
• Hash:表示类型可以进行哈希计算。
• Iterator:表示类型可以进行迭代。
• From 和 Into:表示类型可以进行类型转换。
• AsRef 和 AsMut:表示类型可以进行引用转换。
• Deref 和 DerefMut:表示类型可以进行解引用操作。
动态分发
动态分发就是在形参定义的时候使用trait bound来定义类型,只要实现了某类trait,那在虚表中就一定有对应方法的指针,在调用时候限制只能使用实现了某类trait这部分行为,那无论传过来的是什么类型,只要是实现了某类trait那就是合法的
fn notify(item: &dyn Summary) {
println!("Breaking news! {}", item.summarize());
}