Need help understanding which type constraints to use for num_enum

Hello,
I am fairly new to Rust and I am trying to convert a snippet of Rust code into a version that's up to date with the current crate version, as well as change it slightly to operate on the self instead of defining a new free function

Here's a link to the code

#[derive(Debug, Copy, Clone, FromPrimitive)]
enum Direction {
    NORTH = 0,
    SOUTH,
    EAST,
    WEST,
}

fn turn(d: Direction) -> Direction {
    match FromPrimitive::from_u8(d as u8 + 1) {
        Some(d2) => d2,
        None => FromPrimitive::from_u8(0).unwrap(),
    }
}

I figured out how to update the exact function with crate num_enum version 0.7.3

use num_enum::{FromPrimitive, TryFromPrimitive};

//#[derive(Debug, Copy, Clone, FromPrimitive)]
#[derive(Debug, Default, Copy, Clone, TryFromPrimitive)]
#[repr(u8)]
enum Direction {
    #[default]
    NORTH = 0,
    SOUTH,
    EAST,
    WEST,
}

fn turn(d: Direction) -> Direction {
    match TryFromPrimitive::try_from_primitive(d as u8 + 1) {
        Ok(res) => res,
        Err(_) => Direction::default(),
    }
}

But I'm having some trouble with defining it on the self instead. Here's what I've tried so far:

//impl<T> Direction where T: From<Direction> + Add<u8> {
impl<T: Add<u8>> Direction where T: From<Direction>  {
    fn next(self) -> Self {
        //let test = <Direction as Into<T>>::into(self) + 1u8;
        let test = <Direction as Into<T>>::into(self);
        //let test = self.into() + 1u8;
        self
    }
}

I added the T type param when rustc suggested let test = <Direction as Into<T>>::into(self); when I tried to do let test = self.into() + 1u8;. Of course, I don't expect rustc to give a suggestion for each step. Specifically, I'm confused about the error: the type parameter T is not constrained by the impl trait, self type, or predicates as I thought that the Add<u8> on that line would constrain it.

The method equivalent of the fn turn() you have is

impl Direction {
    fn turn(self) -> Direction {
        match TryFromPrimitive::try_from_primitive(self as u8 + 1) {
            Ok(res) => res,
            Err(_) => Direction::default(),
        }
    }
}

There should not be any added type parameter.

N.b. I didn't test against the crate.


You don't need generics or bounds.

#[derive(Debug, Copy, Clone, Default, TryFromPrimitive)]
#[repr(u8)]
enum Direction {
    #[default]
    NORTH = 0,
    // ...
}

impl Direction {
    fn next(self) -> Self {
        let number = self as u8; // u8::from(self);
        Self::try_from_primitive(number + 1)
            .unwrap_or_default()
    }
}

From looking at the docs and poking around the proc macros, I would have expected this to work:

#[derive(Debug, Copy, Clone, Default, IntoPrimitive, TryFromPrimitive)]
#[repr(u8)]
enum Direction { /* ... */ }

// ...
        let number = u8::from(self);

Because IntoPrimitive looks to actually impl From<TheEnum> for u8. But I also found this issue, so that may not compile. (You can give it a shot and see if the issue is accurate.)


Parameters are constrained by being in a parameter of the trait, a parameter of the implementing type, or by associated type equality on something else that is constrained. The idea is that given the implementing type and the implemented trait (including all parameters of both), T has to be uniquely identifiable.

Here you don't even have a trait, you have a type Direction with no parameters. Trying to be generic won't help.

The compiler was trying to suggest you do something like

        let test = <Direction as Into<u8>>::into(self);
        //                            ^^
        // A specific type, not a generic (but the compiler didn't know which
        // specific type, so it just put `T` in there)

But that won't work unless the implementation exists.

1 Like

smh I made this way too complicated. Thank you!

let number = u8::from(self);

Yes, I get a similar error

error[E0277]: the trait bound `u8: From<Direction>` is not satisfied
  --> src/main.rs:26:22
   |
26 |         let number = u8::from(self);
   |                      ^^ the trait `From<Direction>` is not implemented for `u8`
   |
   = help: the following other types implement trait `From<T>`:
             `u8` implements `From<Char>`
             `u8` implements `From<bool>`