索引 Index 与 IndexMut
用于实现 container[index]
这样的索引操作, 通过 *container.index(index)
以及
*container.index_mut(index)
方法.
它们常用于容器类中, 用于访问容器中的某个或者某些元素.
它们的定义如下:
#![allow(unused)] fn main() { pub trait Index<Idx>where Idx: ?Sized,{ type Output: ?Sized; // Required method fn index(&self, index: Idx) -> &Self::Output; } pub trait IndexMut<Idx>: Index<Idx>where Idx: ?Sized,{ // Required method fn index_mut(&mut self, index: Idx) -> &mut Self::Output; } }
可以看到, IndexMut<T>
是对 Index<T>
的扩展, 它返回一个可变更引用 (mutable reference).
比如:
#![allow(unused)] fn main() { use std::ops::{Index, Range}; let slice = vec![1, 1, 2, 3, 5]; assert_eq!(slice[1..4], *slice.index(Range{start:1, end: 4})); }
可以看到, container[index]
这种写法只是个语法糖.
与 C++ 的区别
在 C++ 中, index
操作符是可以直接向容器中插入新的值, 但根据上面的定义可以看出, IndexMut
只是
返回一个可变引用, 只能用于修改已有值, 不能用于插入新值.
以下代码就会报出 index out of bounds
的错误:
let mut slice = vec![];
slice[0] = 1;
slice[1] = 4;
assert_eq!(slice.len(), 2);
下面的 C++ 代码可以正常编译和运行, 也比较符合常见编程语言的预期操作(例如Python也类似):
#include <cassert>
#include <map>
int main() {
std::map<std::string, double> persons = {};
persons["Joe"] = 1.74;
persons["Allen"] = 1.71;
assert(persons.size() == 2);
return 0;
}
但当把它用 Rust 重写之后, 并不能被编译:
use std::collections::BTreeMap;
let mut persons = BTreeMap::<String, f64>::new();
persons["Joe"] = 1.74;
persons["Allen"] = 1.71;
assert_eq!(persons.len(), 2);
需要显式地插入元素:
#![allow(unused)] fn main() { use std::collections::BTreeMap; let mut persons = BTreeMap::<String, f64>::new(); persons.insert("Joe".to_owned(), 1.74); persons.insert("Allen".to_owned(), 1.71); assert_eq!(persons.len(), 2); }
一个完整的示例
#![allow(unused)] fn main() { use std::ops::{Index, IndexMut}; #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum Side { #[default] Top, Right, Bottom, Left, } #[derive(Debug, Default, Clone, PartialEq)] pub struct Margins { pub top: f32, pub right: f32, pub bottom: f32, pub left: f32, } impl Index<Side> for Margins { type Output = f32; fn index(&self, index: Side) -> &Self::Output { match index { Side::Top => &self.top, Side::Right => &self.right, Side::Bottom => &self.bottom, Side::Left => &self.left, } } } impl IndexMut<Side> for Margins { fn index_mut(&mut self, index: Side) -> &mut Self::Output { match index { Side::Top => &mut self.top, Side::Right => &mut self.right, Side::Bottom => &mut self.bottom, Side::Left => &mut self.left, } } } }