Trying to provide a wrapper for Vec<> and failing

I have a List type that wraps a Vec and adds a couple of read-only fields and (eventually) some additional methods. I really just want to delegate all vec-related code (len(), get(), , etc.) to the contained vec, but I'm stuck. Here's what I have so far (and it won't build):

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

fn main() {}

#[derive(Clone, Debug)]
pub enum Value {
    Bool(bool),
    Bytes(Vec<u8>),
    Int(i64),
    List(List),
    Real(f64),
    Str(String),
}

pub type Row = Vec<Option<Value>>;

#[derive(Clone, Debug)]
pub struct List {
    vtype: String,
    comment: String,
    values: Row,
}

impl List {
    pub fn new(vtype: &str, comment: &str) -> Result<Self> {
        if !vtype.is_empty() {
            //check_name(vtype)?;
        }
        Ok(List {
            vtype: vtype.to_string(),
            comment: comment.to_string(),
            values: Row::new(),
        })
    }

    pub fn vtype(&self) -> &str {
        &self.vtype
    }

    pub fn comment(&self) -> &str {
        &self.comment
    }

    pub fn len(&self) -> usize {
        self.values.len()
    }

    pub fn is_empty(&self) -> bool {
        self.values.is_empty()
    }

    pub fn get(&self, row: usize) -> Option<&Value> {
        self.values[row].as_ref()
    }

    /*
    pub fn get_mut(&mut self, row: usize) -> &mut Option<Value> {
        // ???
    }
    */

    pub fn push(&mut self, value: Option<Value>) {
        self.values.push(value);
    }
}

impl Default for List {
    fn default() -> Self {
        List {
            vtype: "".to_string(),
            comment: "".to_string(),
            values: Row::new(),
        }
    }
}

impl Index<usize> for List {
    type Output = &Option<Value>;

    fn index(&self, row: usize) -> &Self::Output {
        self.get(row)
    }
}

/*
impl IndexMut<usize> for List {
    type Output = &Option<Value>;

    fn index(&self, row: usize) -> &mut Self::Output {
        // ???
    }
}
*/

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
warning: unused import: `IndexMut`
 --> src/main.rs:2:23
  |
2 | use std::ops::{Index, IndexMut};
  |                       ^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

error[E0106]: missing lifetime specifier
  --> src/main.rs:79:19
   |
79 |     type Output = &Option<Value>;
   |                   ^ expected named lifetime parameter
   |
help: consider introducing a named lifetime parameter
   |
79 |     type Output<'a> = &'a Option<Value>;
   |                ++++    ++

For more information about this error, try `rustc --explain E0106`.
warning: `playground` (bin "playground") generated 1 warning
error: could not compile `playground` due to previous error; 1 warning emitted

The return type of index is already &Self::Output. Thus you can make type Output = Option<Value>.

That doesn't work because <[T]>::get() returns Option<&T>, not &Option<T>.

Edit: that does work if you idiomatically implement a panicking Index by delegating to the underlying Index impl itself, but not if you are trying to make it fallible. So you can do this:

impl Index<usize> for List {
    type Output = Option<Value>;

    fn index(&self, row: usize) -> &Self::Output {
        &self.values[row]
    }
}

but not this:

impl Index<usize> for List {
    type Output = Option<Value>;

    fn index(&self, row: usize) -> &Self::Output {
        self.values.get(row)
    }
}
1 Like

Thank you, that works great.

Although ironically, the syntax of usage is less convenient than using get(), e.g.:

        assert_eq!(l1.get(1).unwrap().as_int().unwrap(), -919);
// vs.
        assert_eq!(l1[1].as_ref().unwrap().as_int().unwrap(), -919);

For completeness using your ideas I did the setters too:

    pub fn get_mut(&mut self, row: usize) -> &mut Option<Value> {
        &mut self.values[row]
    }

impl IndexMut<usize> for List {
    fn index_mut(&mut self, row: usize) -> &mut Self::Output {
        &mut self.values[row]
    }
}

I am puzzed that you must not have a type Output line in impl IndexMut even though one's needed for impl Index.
But very glad it all works.
Thanks again.

IndexMut<Idx> has Index<Idx> as a supertrait (you must have implemented Index<Idx> to implement IndexMut<Idx>), and the full signature of IndexMut::<Idx>::index_mut is

fn index_mut(&mut self, index: Idx) -> &mut <Self as Index<Idx>>::Output;

That is, IndexMut has no associated type Output of its own; it uses the one defined in Index.

1 Like

Ah, thank you.