61、Index trait(索引trait)

Indexing

 

上一节的练习中,Ticket::store根据给定的TicketId返回Option<&Ticket>

我们之前已经学过了如何使用Rust的索引语法访问数组和vector的元素。

let v = vec![0, 1, 2];
assert_eq!(v[0], 0);

 

我们如何为TicketStore提供相同的体验?我们需要实现一个trait,就是Indextrait。

 

Index

 

这是Indextrait在Rust标准库中的定义:

// Slightly simplified
// 有稍微的简化
pub trait Index<Idx>
{
    type Output;

    // Required method
    // 必须的方法
    fn index(&self, index: Idx) -> &Self::Output;
}

 

他有两个关键点:

  • 一个泛型参数Idx表示代表索引类型
  • 一个关联类型Output,表示我们使用索引检索到的类型。

 

注意,index 方法不会返回 Option。其假设是,当你尝试访问不存在的元素时,该方法会触发 panic,这一点与数组和 Vec 的索引操作行为一致。

 

练习

 

这一次的练习就是让我们把上一节的get方法改写为Index trait。

 

题目如下:

// TODO: Implement `Index<&TicketId>` and `Index<TicketId>` for `TicketStore`.

use std::ops::Index;
use ticket_fields::{TicketDescription, TicketTitle};

#[derive(Clone)]
pub struct TicketStore {
    tickets: Vec<Ticket>,
    counter: u64,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TicketId(u64);

#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
    pub id: TicketId,
    pub title: TicketTitle,
    pub description: TicketDescription,
    pub status: Status,
}

#[derive(Clone, Debug, PartialEq)]
pub struct TicketDraft {
    pub title: TicketTitle,
    pub description: TicketDescription,
}

#[derive(Clone, Debug, Copy, PartialEq)]
pub enum Status {
    ToDo,
    InProgress,
    Done,
}

impl TicketStore {
    pub fn new() -> Self {
        Self {
            tickets: Vec::new(),
            counter: 0,
        }
    }

    pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
        let id = TicketId(self.counter);
        self.counter += 1;
        let ticket = Ticket {
            id,
            title: ticket.title,
            description: ticket.description,
            status: Status::ToDo,
        };
        self.tickets.push(ticket);
        id
    }

    pub fn get(&self, id: TicketId) -> Option<&Ticket> {
        self.tickets.iter().find(|&t| t.id == id)
    }
}

/* TODO */

/* TODO */

 

题解:

impl Index<TicketId> for TicketStore {
    type Output = Ticket;
    fn index(&self, id: TicketId) -> &Self::Output {
        &self.tickets[id.0 as usize]
    }
}

impl Index<&TicketId> for TicketStore {
    type Output = Ticket;
    fn index(&self, id: &TicketId) -> &Self::Output {
        &self.tickets[id.0 as usize]
    }
}

 

这道题让我们为TicketStore实现Index trait。而且对两个类型TicketId&TicketId都要实现这个功能。

那么我们直接起手,常规思路。

关于index函数体里面那个代码,.0其实是最开始学结构体的时候,讲的一种结构体内部元素的访问方式,对于结构体内部的匿名字段,可以直接用.+索引的方式来访问。所以我们用id.0访问到了实际的ID数字,但是索引必须是usize类型,于是我们再进行一下类型转换,然后就可以正常访问了。

 

但是官方的答案是这个:

impl Index<TicketId> for TicketStore {
    type Output = Ticket;

    fn index(&self, id: TicketId) -> &Self::Output {
        self.tickets.iter().find(|&t| t.id == id).unwrap()
    }
}

impl Index<&TicketId> for TicketStore {
    type Output = Ticket;

    fn index(&self, id: &TicketId) -> &Self::Output {
        &self[*id]
    }
}

他是用的迭代器和组合器的方式,用到了前面几节的知识,看来RustRover为了让我们掌握迭代器和组合器这一块的内容,真是煞费苦心。

 

这一节的学习就先到这里了。

 

阅读剩余
THE END