Avoiding redundant code when implementing Index and IndexMut in Rust

Currently, implementing a trait std::ops::IndexMutfor a type in Rust requires me to implement a property as well std::ops::Index. The bodies of these implementations are almost identical. For instance:

use std::ops::{Index, IndexMut};

enum IndexType {
    A,
    B,
}

struct Indexable {
    a: u8,
    b: u8,
}

impl Index<IndexType> for Indexable {
    type Output = u8;
    fn index<'a>(&'a self, idx: IndexType) -> &'a u8 {
        match idx {
            IndexType::A => &self.a,
            IndexType::B => &self.b,
        }
    }
}

impl IndexMut<IndexType> for Indexable {
    fn index_mut<'a>(&'a mut self, idx: IndexType) -> &'a mut u8 {
        match idx {
            IndexType::A => &mut self.a,
            IndexType::B => &mut self.b,
        }
    }
}

fn main() {}

This works, and obviously for trivial types this is not a serious problem, but for more complex types with more interesting indexing it quickly becomes time-consuming and error prone. I scratch my head, trying to find a way to unify this code, but nothing jumps at me, and yet I feel that there should / should be a way to do this, without having to copy and paste. Any suggestions? What am I missing?

+4
source share
1

, , Rust . , , :

macro_rules! as_expr {
    ($e:expr) => { $e };
}

macro_rules! borrow_imm { ($e:expr) => { &$e } }
macro_rules! borrow_mut { ($e:expr) => { &mut $e } }

macro_rules! impl_index {
    (
        <$idx_ty:ty> for $ty:ty,
        ($idx:ident) -> $out_ty:ty,
        $($body:tt)*
    ) => {
        impl ::std::ops::Index<$idx_ty> for $ty {
            type Output = $out_ty;
            fn index(&self, $idx: $idx_ty) -> &$out_ty {
                macro_rules! index_expr { $($body)* }
                index_expr!(self, borrow_imm)
            }
        }

        impl ::std::ops::IndexMut<$idx_ty> for $ty {
            fn index_mut(&mut self, $idx: $idx_ty) -> &mut $out_ty {
                macro_rules! index_expr { $($body)* }
                index_expr!(self, borrow_mut)
            }
        }
    };
}

enum IndexType { A, B }

struct Indexable { a: u8, b: u8 }

impl_index! {
    <IndexType> for Indexable,
    (idx) -> u8,
    ($this:expr, $borrow:ident) => {
        match idx {
            IndexType::A => $borrow!($this.a),
            IndexType::B => $borrow!($this.b),
        }
    }
}

fn main() {
    let mut x = Indexable { a: 1, b: 2 };
    x[IndexType::A] = 3;
    println!("x {{ a: {}, b: {} }}", x[IndexType::A], x[IndexType::B]);
}

: index/index_mut , , &expr &mut expr, self ( ), self Rust, , .

+5

All Articles