Rust简明教程第四章-其他类型&泛型

观看B站软件工艺师杨旭的rust教程学习记录,有删减有补充

枚举

枚举类型可以让我们的程序使用一些固定长度和固定数值的变量值范围

enum 枚举

枚举类型中的值被称为变体

enum IpAddrKind{
    V4,//变体
    V6,//变体
}
fn main(){
    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;
    route(four);
    route(six);
    route(IpAddrKind::V6);
}
fn route(ip_kind:IpAddrKind){
}

struct存储数据

enum IpAddrKind {
    V4, //变体
    V6, //变体
}
struct IpAddr {
    kind: IpAddrkind,
    address: String,
}
fn main() {
    let home = IpAddr {
        kind: IpAddrKind::V4,
        address: String::from("127.0.0.1"),
    };
    let loopback = IpAddr {
        kind: IpAddrKind::v6,
        address: String::from("::1"),
    };
}

将数据添加到变体

enum IpAddrKind{
    V4(u8,u8,u8,u8),
    V6(String),
}
fn main() {
    let home=IpAddrKind::V4(127.0.0.1);
    let loopback = IpAddrKind::V6(String::from("::1"));
}

嵌入其他类型

enum Message {
    Quit,
    Move { x: i32, y: i32 }, //匿名结构体
    Write(String),
    ChangeColor(i32, i32, i32),
}
fn main() {
    let q = Message::Quit;
    let m = Message::Move { x: 12, y: 12 };
    let w = Message::Write(String::from("Hello"));
    let c = Mssage::ChangeColor(0, 255, 255);
}

为枚举定义方法

enum Message {
    Quit,
    Move { x: i32, y: i32 }, //匿名结构体
    Write(String),
    ChangeColor(i32, i32, i32),
}
impl Message {
    fn call(&self) {
        println!("这是call方法"); //这是call方法
    }
}
fn main() {
    let q = Message::Quit;
    let m = Message::Move { x: 12, y: 12 };
    let w = Message::Write(String::from("Hello"));
    let c = Message::ChangeColor(0, 255, 255);
    m.call();
}

Option 枚举

Option 枚举类型来表示可能具有值或者可能没有值

  • Rust 并没有空值,不过它拥有一个可以编码存在或不存在概念的枚举
  • Rust强制您在使用可能为空的值时进行显式处理,以防止出现空值错误(Null Pointer Errors)或空值引起的其他问题
enum Option<T> {
    None,//包含在prelude中,可以直接使用
    Some(T),//包含在prelude中,可以直接使用
}

Some代表可能有值,可能没值
None代表没有值

fn main() {
    let some_string = Some(5);//`Option<i32>` 类型的变量
    let some_string = Some("A String");//`Option<&str>` 类型的变量
    let absent_number: Option<i32> = None;//`Option<i32>` 类型的空变量,变量不包含具体的值
}
  • 要使用Option<T>中的T必须将它转换为T
fn main() {
    let maybe_number: Option<i32> = Some(42);
    // 转换 Option<i32> 到 i32
    let number: i32 = maybe_number.unwrap();//尝试从 `Some` 变体中提取值,并返回该值。如果`Option`是 `None` 变体,`unwrap()`方法会产生panic(崩溃)
    println!("{}", number);//42
}

标准库类型

Vector类型 动态数组

声明Vector

fn main() {
    let v1: Vec<i32> = Vec::new();
    //vec!是一个宏
    let v2 = vec![1, 2, 3];
}

访问元素

fn main() {
    let v = vec![1, 2, 3];
    println!("{:?}", v); //[1, 2, 3],vector没有实现display,所以使用格式化输出
    //索引访问
    println!("{}", &v[0]); //1
    //match get访问
    match v.get(2) {
        Some(third) => println!("{}", third), //3,有值就绑定到返回值third上
        None => println!("没有这个值"),
    }
    //for遍历
    for i in &v{
        println!("{}",i);//123
    }
}

添加删除元素

fn main() {
    let mut v = Vec::new();
    //添加元素
    v.push(1);
    v.push(2);
    v.push(3);
    v.push(4);
    //删除元素
    v.pop();
    println!("{:?}", v); //[1, 2, 3]
}

使用enum在vector中存储不同类型的数据

#[derive(Debug)]
enum SpreadsheetCell {
    Int(i32),
    Folat(f64),
    Text(String),
}
fn main() {
    let row = vec![
        SpreadsheetCell::Int(3),
        SpreadsheetCell::Text(String::from("blue")),
        SpreadsheetCell::Folat(10.12),
    ];
    //for遍历
    for i in &row {
        println!("{:#?}", i); //Int(3,)Text("blue",)Folat(10.12,)
    }
}

String类型 可变字符串

  • String 被存储为由byte(字节)组成的 vector(Vec<u8>),但是不允许索引访问!(UTF-8使用1到4个字节编码Unicode字符,索引访问无法确定字符的边界)
  • UTF-8编码

String类型可获得所有权:OsStringCString

&str字符串切片对String类型的借用:SsStrCStr
声明String

fn main() {
    let s1 = "初始化字符串"; //借用的切片
    let s2 = s1.to_string(); //String类型,由于切片借用不获得所有权,这里实际是&&s1.to_string()
    let s3 = "hello world s3".to_string(); //String类型
    let s4 = String::from("hello world s4"); //String类型
    println!("{},{},{},{}", s1, s2, s3, s4); //初始化字符串,初始化字符串,hello world s3,hello world s4
}

更新String

fn main() {
    let mut s1 = String::from("张");
    //将字符串切片附加到String
    s1.push_str("三");
    println!("{}", s1); //张三
                        //将单个字符附加到String,注意字符是''
    s1.push('🥹');
    println!("{}", s1); //张三🥹
    //拼接字符串
    let s2 = String::from("李四");

    //+的实现:pub fn add(self, rhs: Rhs) -> Self::Output
    let s3 = s1 + &s2; //add函数取得s1所有权并附加s2,然后将s1所有权move到s3,s1失效,s2是借用,不改变所有权
    println!("{}", s2); //张三🥹李四

    //format!宏会返回字符串
    let s4 = format!("{}-{}", s2, s3);
    println!("{}", s4); //李四-张三🥹李四
}

切割String

切片

fn main(){
    let s1 = "hello world";
    let s1 = &s1[1..4];
    println!("{}",s1);//hell
}

对于字符编码会不一致的字符串,rust无法推断字符长度,要谨慎操作,以下代码会发生panic,6无法确定🐕的边界

fn main(){
    let s1 = "hello🐕🦊";
    let s1 = &s1[0..6];
    println!("{}",s1);//hell
}

HashMap集合 哈希表

key:value存储数据

  • HashMap不在prelude中,需要use导入
  • HashMap是同构的,所有的key必须是同一类型,所有的value必须是同一类型
  • 每个key只能对应一个value

声明HashMap

use std::collections::HashMap;
fn main() {
    let mut scores: HashMap<String, i32> = HashMap::new();
    scores.insert(String::from("blue"), 10);
    scores.insert(String::from("blue"), 50);
}

访问

use std::collections::HashMap;
fn main() {
    let mut map: HashMap<String, i32> = HashMap::new();
    map.insert(String::from("blue"), 10);
    map.insert(String::from("red"), 50);

    let key = String::from("blue");
    let value = map.get(&key);
    match value{
        Some(s)=>println!("{}",s),//10
        _=>println!("不存在!"),
    }

    for(k,v)in &map{
        println!("{}:{}",k,v);//blue:10 red:50
    }
}

更新HashMap

use std::collections::HashMap;
fn main() {
    let mut map1: HashMap<String, i32> = HashMap::new();
    map1.insert(String::from("blue"), 10);
    map1.insert(String::from("blue"), 50);
    for (k, v) in &map1 {
        println!("{}:{}", k, v); //blue:50
    }
    println!("******************");
    //使用entry判断key是否存在,存在则不更新并返回对应的可变引用,不存在则更新并返回对应的可变应用
    let mut map2 = map1.clone();
    map2.entry(String::from("blue")).or_insert(40);
    map2.entry(String::from("red")).or_insert(60);
    for (k, v) in &map2 {
        println!("{}:{}", k, v); //red:60 blue:50
    }
}

例子

use std::collections::HashMap;
fn main() {
    let text = "hello world wonderful world";
    let mut map = HashMap::new();
    for word in text.split_whitespace() {
        //以空格分割
        let count = map.entry(word).or_insert(0); //存在则不更新并返回对应的可变引用,不存在则更新并返回对应的可变应用
        *count += 1; //解引用改变value的值
    }
    println!("{:?}", map); //{"wonderful": 1, "hello": 1, "world": 2}
}

泛型

提高代码复用能力,消除类型转换,不影响性能

fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
    let mut largest = list[0];
    for &item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}
fn main() {
    let number_list = [12, 4, 1, 5, 48];
    let result = largest(&number_list);
    println!("数组中最大的数字:{}", result);

    let char_list = vec!['a', 'S', 'w', 'r'];
    let result = largest(&char_list);
    println!("最大的字符:{}", result);
}

泛型结构体

struct Point<T, U> {
    x: T,
    y: U,
}
fn main() {
    let integer = Point { x: 5, y: 12.54 };
    let float = Point { x: 1.0, y: 4.0 };
}

泛型方法

struct Point<T, U> {
    x: T,
    y: U,
}
impl<T, U> Point<T, U> {
    fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}
fn main() {
    let p1 = Point { x: 5, y: 4 };
    let p2 = Point {
        x: "Hello",
        y: "World",
    };
    let p3 = p1.mixup(p2);
    println!("混合两点的x、y(x:{},y:{})", p3.x, p3.y); //混合两点的x、y(x:5,y:World)
}

Associated Types 关联类型

在 trait 中定义类型占位符的机制,可以在实现 trait 时具体化

// 定义一个 Container trait,其中包含一个关联类型 Item
trait Container {
    type Item;//占位符,用于表示容器中元素的类型
    fn first(&self) -> Option<&Self::Item>;// 获取容器中的第一个元素的引用
    fn last(&self) -> Option<&Self::Item>;// 获取容器中的最后一个元素的引用
}
// 对 Vec<T> 实现 Container trait
impl<T> Container for Vec<T> {
    type Item = T;//将 Item 关联类型具体化为 T,即 Vec 中元素的类型
    fn first(&self) -> Option<&Self::Item> {
        self.get(0)
    }
    fn last(&self) -> Option<&Self::Item> {
        self.get(self.len() - 1)
    }
}
// 对自定义的容器 MyContainer 实现 Container trait
struct MyContainer<T> {
    elements: Vec<T>,
}
impl<T> Container for MyContainer<T> {
    type Item = T;//将 Item 关联类型具体化为 T
    fn first(&self) -> Option<&Self::Item> {
        self.elements.first()
    }
    fn last(&self) -> Option<&Self::Item> {
        self.elements.last()
    }
}
fn main() {
    let v = vec![1, 2, 3, 4];
    let my_container = MyContainer {
        elements: vec![10, 20, 30, 40],
    };

    // 使用 Vec<T> 实现的 Container trait
    println!("vec第一个元素: {:?}", v.first()); //vec第一个元素: Some(1)
    println!("vec最后一个元素: {:?}", v.last()); //vec最后一个元素: Some(4)

    // 使用 MyContainer 实现的 Container trait
    println!("MyContainer第一个元素: {:?}", my_container.first()); //MyContainer第一个元素: Some(10)
    println!("MyContainer最后一个元素: {:?}", my_container.last()); //MyContainer最后一个元素: Some(40)
}
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cci497

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值