前端同学的rust入门(五)--复合类型

rust的复合类型,类似于TS中的对象类型,主要有String,Struct,元组,数组,枚举,HasMap。

字符串

之前提到过,字符串String和&str是两种不同的类型。这里说的字符串,是指String。 我们可以将&str转换为String

ini 复制代码
 let name:String = String::form("Hello");
 let name2:String = "Hello".to_string();

我们可以利用切片,将String转换为&str

ini 复制代码
let s = String::from("hello,world!");
let hello = &s[0..5];//这是切片语法

索引操作的陷阱

在对字符串使用索引相关的时需要格外小心,下标索引必须落在字符之间的边界位置,也就是 UTF-8 字符的边界,例如中文在 UTF-8 中占用三个字节,下面的代码就会崩溃:

let 复制代码
let a = &s[0..2];
println!("{}",a); '

String 类型具有很多操作

rust 复制代码
fn main() {
    let mut s = String::from("Hello ");
    s.push_str("World"); // 追加字符
    println!("push: {}", s);
    s.insert(5, ','); // 插入字符
    println!("insert:  {}", s);
    let new_string_replace = s.replace("Hello", "HELLO"); //字符串替换,返回一个新的字符串
    println!("new_string_replace:  {}  s:  {}", new_string_replace, s);
    let new_string_replacen = s.replacen("World", "WORLD", 1); //与replace相似,第三个参数表示替换几次
    println!("new_string_replace:  {}  s:  {}", new_string_replacen, s);
    let p1 = s.pop(); //删除并返回字符串的最后一个字符。返回值是下文会讲述的Option类型
    println!("pop:  {:?} s:  {}", p1, s);
    let remove = s.remove(0); //删除并返回某个位置的字符,注意可能会发生索引异常
    println!("remove:  {:?} s:  {}", remove, s);
    let truncate = s.truncate(3); //删除字符串中从指定位置开始到结尾的全部字符
    println!("truncate:  {:?} s:  {}", truncate, s);
    s.clear(); //清空字符串
    println!("s:  {}", s);

}

字符串相加

  1. 和JS一样,可以使用+进行相加,但是对其类型有要求。相加的第一个参数必须是String类型,后面的参数则是&str类型或者&String类型。
ini 复制代码
fn main() {
    let hello = String::from("Hello");
    let world = "World";
    let rust = ",rust";
    let pointer: &String  = & String::from("WS");
    let result = hello+ &world+rust+pointer;
    let mut result = result + "!"; // `result + "!"` 中的 `result` 是不可变的
    result += "!!!";
    println!("拼接结果: {}", result);
}
  1. 使用format进行格式化,format的用法有点类似于println
ini 复制代码
  let s1 = "Hello";
    let s2 = String::from("World");
    let s3 = String::from("And Rust");
    let s = format!("{} {} {}", s1, s2,s3); 
    println!("{}", s);

Struct

Struct,也叫结构体,这个概念类似于TS中的class,用来定义一个对象。 我们看一下声明

rust 复制代码
//rust
struct Person {
    age: u32,
    username: String,
    isBoy:bool,
}

和TS的语法非常类似,但是struct没有可选属性,所有的属性在初始化时必填

typescript 复制代码
//ts
class User {
    age: number,
    username: string,
    isBoy:true,
    constructor(age:number, username:string) {
        this.age = age;
        this.username = username;
  }
}

两者的实例化方式也有所不同:

csharp 复制代码
// rust
let user1 = User {
    username: String::from("shengjiang"),
    age: 18,
    isBoy:true,
};
    
//TS 
let user1 =  new User(18,'shengjiang')

两者在访问和属性赋值上是一致的,甚至rust也提供了解构语法

ini 复制代码
//rust,不要忘了mut
let mut user1 = User {
    username: String::from("shengjiang"),
    age: 18,
};
user1.age =20;
let user2 = User {
    age: 30,
    ..user1 //注意,这里只有两个点
};

//TS 
const user1 =  new User(18,'shengjiang')
user1.age=20;
const user2:User = {
    username: String::from("natie"),
    ...user1// 这里有三个点
};

这里,需要注意,我们将user1解构赋值给了user2,这时发生了所有权转移,如第四章所说,这里只有String类型的username属性发生了所有权转移,其他属性因为是基础类型,因此不涉及,这时候直接访问user1会报错,但是访问未发生所有权转移的字段却依然可行

rust 复制代码
let mut user1 = User {
   username: String::from("shengsuannatie"),
   age: 18,
};
let user2 = User {
   age: 30,
   ..user1 //注意,这里只有两个点
};
println!("{}", user1.age); // 正常 
println!("{:?}", user1);// 报错 

方法

在TS中,使用Class,可以在里面定义方法,struct却不行。但是rust通过impl关键字,来达到这种效果:

rust 复制代码
struct Person {
    first_name: String, //注意,属性命名必须使用蛇形命名,既'***_**_'
    second_name: String,
}

impl Person {
    fn say_my_name(&self) -> String { //注意,方法名也必须使用蛇形命名
        format!("{}{}", self.first_name, self.second_name) //&slef的概念就是JS中的this,但是它的作用域比JS的更加清晰,更加容易理解 
    }
}

fn main() {
    let person = Person {
        first_name: String::from("生姜"),
        second_name: String::from("拿铁"),
    };

    println!("Say My Name {}", person.say_my_name());
}
  

枚举

概念上与TS的枚举一致,写法也类似:

ini 复制代码
enum Sex {
    Boy,
    Girl,
  }
let boy = Sex::Boy;

在TS中我们还可以这么写:

ini 复制代码
enum Sex {
    Boy=1,
    Girl='girl',
  }
let boy = Sex.Boy;

rust中也有类似的实现

rust 复制代码
enum Sex {
    Boy:u32,
    Girl:&str,
  }
let boy = Sex::Boy(1);
let girl = Sex::Girl('gril');

对于强类型语言来说,rust的枚举是一个非常神奇的存在,它的每一个子项都可以对应一个不同的数据结构,在处理某些场景下,几乎像动态语言一样强大。

特殊的枚举

Rust中有一个特殊的枚举,叫Option,前文中String的remove的返回值就是一个Option。

scss 复制代码
  enum Option<T> {
    Some(T),
    None,
}

其中None表示没有,Rust中没有Null,使用枚举None表达同等的含义。 在使用时,可以省略Option::,直接使用Some或者None。

ini 复制代码
let some_number = Some(5);
let some_string = Some("a string"); 
let absent_number: Option<i32> = None;

在rust的api中,如果一个方法不能确保一定有值返回,就会返回一个Option枚举,便于我们做异常捕捉。一般会搭配match关键字使用。我们后面会介绍到。

元组

rust元组的概念与TS的元祖也是一致的。 看一个简单的示例:

ini 复制代码
let x: (u32, u32, u32) = (30, 1, 1); 
let age = x.0;
let grade = x.1;

元组和struct

元组可以和strcuct结合

rust 复制代码
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

静态数组

rust的数组有两类,一种是静态数组,一种是动态数组。 静态数组是存放在栈上的,要求比如类型一致,且需要在初始化的时候确定长度,且元素要保持相同的类型。 动态数据是存放在堆上的,在初始化的时候不需要确定长度。

less 复制代码
let a = [1, 2, 3, 4, 5];//直接初始化,rust可以推断出类型
let a: [i32; 5] = [1, 2, 3, 4, 5]; //手动声明类型
let a = [3; 5];//表示初始长度为3,且每个元素都是5

静态数组的存取效率比较高,因为存放在栈上,但是静态数组是不允许越界访问的:

less 复制代码
let a = [1, 2, 3, 4, 5];
let b = a[6] //报错

数组同样支持切片,之前说到字符串的切片是危险的,因为不同文字所占的长度不一样。而数组是安全的,因为它的每一个元素所占长度一样:

ini 复制代码
let a: [i32; 5] = [1, 2, 3, 4, 5]; 
let slice: &[i32] = &a[1..3];

因为是静态的数组,所以没有push,pop之类会改变数组长度的操作。

动态数组

动态数组类型用 Vec<T> 表示。动态数组也只能存储相同类型的元素,如果你想存储不同类型的元素,可以使用之前讲过的枚举类型或者特征对象。(特征类似于TS中interface,后面章节会讲) 我们看一下动态数组的常用API

rust 复制代码
let mut v: Vec<i32> = Vec::new(); //实例化,此时要声明类型
let v = vec![1, 2, 3]; //实例化的同时进行初始化,此时可以省略类型声明
v.push(1);// 添加
let third: &i32 = &v[2]; //下标访问

除了下标访问,还可以使用get来访问,但是get返回的结果是一个Option枚举:

rust 复制代码
let v = vec![1, 2, 3, 4, 5]; 
match v.get(6) {
    Some(num) => println!("第6个是 {num}"), 
    None => println!("没有第6")
 }

直接使用下标访问,可能会越界引起运行时错误。但是使用get则不会有这种问题,当你访问一个越界下标时,只会返回一个None。

遍历

rust 复制代码
let mut v = vec![1, 2, 3];
for i in &mut v {
    *i += 1  //注意这里的指针用法
}

排序

动态数组自带了两种排序,一种是稳定排序,一种是非稳定排序。在 稳定 排序算法里,对相等的元素,不会对其进行重新排序。而非稳定排序则不会,因此非稳定排序的速度会比较快,同时占用更少的内存。

ini 复制代码
let mut vec = vec![1, 3, 3, 2, 5]; 
vec.sort_unstable();

像JS数组的sort一样,原生的sort只能支持基础类型的比较,如果是复合类型的元素,如何排序呢? Rust提供了了JS一样的方式,使用sort_by和sort_unstable_by传入函数:

css 复制代码
let mut vec = vec![1, 3, 3, 2, 5]; 
vec.sort_unstable_by(|a, b| a - b);

这里用到了匿名函数,也就是闭包了。

HashMap

HashMap与ES6的Map类似,用法也相似

rust 复制代码
let mut keys = HashMap::new();
keys.insert("key", 1);
keys.insert("key2", 2);
let score: Option<&i32> = keys.get("key");

HashMap和所有权

rust 复制代码
fn main() {
    use std::collections::HashMap;

    let name = String::from("拿铁");
    let age = 18;

    let mut handsome_boys = HashMap::new();
    handsome_boys.insert(name, age);//发生了所有权转移

    println!("{}", name);//这里调用name就错了
    println!("{}岁", age);
}
相关推荐
掘金者阿豪4 分钟前
把业务数据变成共享仪表盘:Metabase可视化与远程访问实践
前端·后端
kyriewen24 分钟前
折腾了半年 AI 编程工作流,最后发现效率瓶颈是桌上那块屏幕
前端·javascript·ai编程
蜗牛前端1 小时前
codex 全流程开发上线的高颜值礼簿小程序
前端·微信小程序
大龄秃头程序员2 小时前
我在图文流 App 里落地双层缓存、弱网降级与 OOM 治理
前端
老王以为2 小时前
React Renderer 分离的多平台架构
前端·react native·react.js
hunterandroid2 小时前
Kotlin Coroutines 与 Flow:让异步任务更清晰
前端
Bigger2 小时前
从零搭建 AI 代码审查服务:一份前端也能看懂的 Python 学习笔记
前端·ci/cd·ai编程
lichenyang4532 小时前
JSAPI、NAPI、Biz、Imp:ASCF Demo 如何真正调用系统能力和 C++ 能力
前端
lichenyang4533 小时前
IPC、JSVM、UIThread、libuv:ASCF 架构图里最容易混的几个词
前端
用户059540174463 小时前
Redis记忆存储故障恢复测试踩坑实录:手动测试让我漏掉了2个一致性Bug
前端·css