Implementation of trait not recognized in scope


#1

I’m trying to solve an old puzzle (from Advent of Code) to learn some Rust. The goal is to calculate the brightness of some grid of lamps. Full description at Advent of Code - Day 6 (2015).

First i define a trait Lamp

pub trait Lamp {
    fn toggle(&mut self);
    fn turn_on(&mut self);
    fn turn_off(&mut self);
    fn brightness(&self) -> u32;
}

I implement the trait for two kinds of lamps

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct SwitchLamp {
    on: bool
}

impl Lamp for SwitchLamp {
    ...
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct DimmerLamp {
    intensity: u32
}

impl Lamp for DimmerLamp {
    ...
}

I want to put the Lamps in a Grid

#[derive(Copy, Clone)]
pub struct Grid<Lamp> {
    grid: [[Lamp; 1000]; 1000]
}

but unfortunately the compiler complains that the methods are not implemented in scope.

impl<Lamp> Grid<Lamp> {
    fn toggle(&mut self, x: usize, y: usize) {
        self.grid[x][y].toggle();
    }
   ...
}

The complete error message reads:

error[E0599]: no method named `toggle` found for type `Lamp` in the current scope
  --> src/day06.rs:98:25
   |
98 |         self.grid[x][y].toggle();
   |                         ^^^^^^
   |
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `toggle`, perhaps you need to implement it:
           candidate #1: `Lamp`

Full code can be found at day06.rs

Can anybody help me understand what i am doing wrong, and help me fix the problem?


#2

I get a different error, and frankly, the one I expected :smile:

error[E0277]: the trait bound `[[Lamp; 1000]; 1000]: std::clone::Clone` is not satisfied

arrays only implement traits up to size 32; you should use a vector instead.


#3

This confuses me even more :slight_smile:

  1. The error message you get, only shows up with the stable compiler. I used the nightly, and didn’t get that message. Is there a change between stable and nightly to explain this?

  2. You say arrays implement traits up to size 32. So i replaced 1000 with 3, to test quickly. But still i get the same errors. (all of them).

  3. I will try to change the array to vectors. But could you please explain to me why it is more appropriate? I assumed that, because the sizes are known in advance and fixed, using arrays would be better?

Thank you!


#5

Try this:

impl<T> Grid<T> where T: Lamp

When you write impl<Lamp>, it introduces a new type variable that happens to have the same name as your trait, but is otherwise not related to it.


#6

Lamp is a trait here. You seem to be expecting it to work like a Java interface, but traits aren’t exactly like that.

[Lamp; 100] doesn’t make sense in Rust, because that’s fixed-size array of any-size elements (you could impl Lamp on bool as well as on [usize; 100000], and you can’t put all of them in the same spot in the array).

In Java every object is indirectly accessed via pointer. In Rust the equivalent is a Box. You can have [Box<Lamp>; 1000] to have Java-like objects accessed via abstract interface.


#7

Arrays in Rust don’t work with traits well. That’s just a technical limitation that will be removed in a future version, but for now they’re an ugly duckling.

For function like your toggle the fixed size of the array doesn’t make a difference, because range of x and y is not fixed, so Rust has to check if they’re within limits anyway (but with vecs for performance it’s slightly better to have one vec of 1000*1000 elements, rather than 1000 nested vecs).


#8

Yes, it was recently changed in Nightly, it’s a important improvement for Rust. On the other hand copying very large fixed-size arrays is often a bad idea. On the third hand vectors of vectors aren’t very efficient. So you can sometimes solve the problem with Vec<[u32; 1000]> This allows the data to be contiguous and avoid the copy-by-value semantics of arrays.

Edit: there are also two different configurations with Box, Box<[[u32; 1000]; 1000]> and Box<[[u32; 1000]]>:

#![feature(box_syntax)]
#![allow(unused_variables)]
fn main() {
    let m1 = box [[0u32; 1000]; 1000];
    let m2 = vec![[0u32; 1000]; 1000].into_boxed_slice();
}