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,就是Index
trait。
Index
这是Index
trait在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为了让我们掌握迭代器和组合器这一块的内容,真是煞费苦心。
这一节的学习就先到这里了。
阅读剩余
版权声明:
作者:CN059
链接:https://www.cn059.com/2025/09/25/61%e3%80%81index-trait%ef%bc%88%e7%b4%a2%e5%bc%95trait%ef%bc%89.html
文章版权归作者所有,未经允许请勿转载。
THE END