0

I want to learn how to use the Index trait for my toy roguelike but can't even get it to work in a useless, dummy scenario.

use std::ops::Index;

// Dummy struct that wraps i32
#[derive(Clone, Copy, Debug)]
pub struct Integer {
    pub val: i32,
}

impl Integer {
    fn new(num: i32) -> Self {
        Integer { val: num }
    }
}

// Using the index operator on an Integer should add the index to the Integer's value.
// let i = Integer::new(20);
// i[20];
// The above code should make it so i.val == 40
impl Index<Integer> for Integer {
    type Output = i32;

    // The error is in the following line:
    fn index(&self, to_add: Integer) -> &Self::Output {
        self.val + to_add.val;
    }
}

// The code causes an error before it reaches this
fn main() {
    let mut i = Integer::new(20);
    let mut n = Integer::new(30);
    println!("i[20] is: {:?}", i[n]);
}

I get this error:

error[E0308]: mismatched types
  --> src/main.rs:23:55
   |
23 |       fn index(&self, to_add: Integer) -> &Self::Output {
   |  _______________________________________________________^
24 | |         self.val + to_add.val;
25 | |     }
   | |_____^ expected &i32, found ()
   |
   = note: expected type `&i32`
              found type `()`

I don't really know what I'm talking about but I guess that the value dies before it reaches the end of the function or something like that? I don't yet fully understand lifetimes.

I know this looks like a "NO IDEA WHAT IM DOING FIX PLS" question, but I'd love to know what I'm doing wrong here so I can learn from it.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
ohmree
  • 375
  • 3
  • 15

1 Answers1

1

Editor's note: This answer applied to the original question before the OP completely changed it. It is no longer applicable to the posted question.


The Index documentation says, emphasis mine:

The Index trait is used to specify the functionality of indexing operations like container[index] when used in an immutable context.

You are attempting to mutate the value, which is simply not possible. You can also tell this from the signature of index:

pub trait Index<Idx> 
where
    Idx: ?Sized, 
{
    type Output: ?Sized;
    fn index(&self, index: Idx) -> &Self::Output;
}

There's no mut anywhere here; you cannot "implement" a trait with a completely different signature! You also cannot change the type of one of the arguments (in this case, self).


Bluntly put, Index is the wrong trait to use. Additionally, idiomatic Rust users would be really upset if you did implement the code this way; we generally aren't of the "reuse an operator for a completely different meaning" crowd.

Instead, this should just be a function with a name:

impl Integer {
    fn increment(&mut self, to_add: i32) {
        self.val += to_add;
    }
}

Alternatively, you could implement DerefMut:

use std::ops::{Deref, DerefMut};

impl Deref for Integer {
type Target = i32;
    fn deref(&self) -> &Self::Target { &self.val }
}

impl DerefMut for Integer {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.val }
}

And then use it like

let mut i = Integer::new(20);
*i += 20;
println!("i[20] is: {:?}", i);
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • I realized that and changed it to just addition instead of mutation, I'll edit the question – ohmree Oct 03 '17 at 12:45
  • @omrisim210 so, you wish to invalidate the answer? That's very frowned on. – Shepmaster Oct 03 '17 at 12:46
  • @omrisim210 for example - https://meta.stackoverflow.com/q/266946/155423 – Shepmaster Oct 03 '17 at 12:47
  • Well, I didn't think about invalidating the answer. Should I ask a new question if this answer works for my case? Perhaps you could edit your answer (as awkward as it sounds)? – ohmree Oct 03 '17 at 12:52
  • 1
    And I understand that what I'm doing is not idiomatic, but I just wanted to learn how to use `Index`. In a real world scenario I wouldn't have even wrapped `i32` in a `struct`. – ohmree Oct 03 '17 at 12:53