前端同学的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);
}
相关推荐
qq13267029402 分钟前
运行Zr.Admin项目(前端)
前端·vue2·zradmin前端·zradmin vue·运行zradmin·vue2版本zradmin
魏时烟1 小时前
css文字折行以及双端对齐实现方式
前端·css
2401_882726482 小时前
低代码配置式组态软件-BY组态
前端·物联网·低代码·前端框架·编辑器·web
web130933203982 小时前
ctfshow-web入门-文件包含(web82-web86)条件竞争实现session会话文件包含
前端·github
胡西风_foxww2 小时前
【ES6复习笔记】迭代器(10)
前端·笔记·迭代器·es6·iterator
前端没钱2 小时前
探索 ES6 基础:开启 JavaScript 新篇章
前端·javascript·es6
m0_748255263 小时前
vue3导入excel并解析excel数据渲染到表格中,纯前端实现。
前端·excel
土豆炒马铃薯。4 小时前
【Vue】前端使用node.js对数据库直接进行CRUD操作
前端·javascript·vue.js·node.js·html5
CC__xy4 小时前
Node二、Node.js 模块化、es6 软件包、Express - 框架
前端·node.js
zpjing~.~4 小时前
CSS 过渡动画效果
前端·css