3

I'm writing a function that will create a menu interface in the terminal. To use the function, it needs to be passed a Vec<menu_option>

#[derive(Clone)]
pub struct menu_option<'a, T> {
    pub command: &'a str,
    pub description: &'a str,
    pub action: menu_option_action<'a, T>,
}

#[derive(Clone)]
pub enum menu_option_action<'a, T> {
    sub_menu(Vec<menu_option<'a, T>>),
    callback(fn(state: T) -> T),
    leave,
}

When I design the menu, it can have multiple levels and there can be repeats of a menu_action. Namely, each layer has a "leave"/"return":

// In my actual code I construct a vector of `menu_option`s
fn example() {
    type StateType = i32;
    let mut state: StateType = 88;
    let leave_option: menu_option<&mut StateType> = menu_option::<&mut StateType> {
        command: "l",
        description: "leave",
        action: menu_option_action::leave,
    };

    let a = leave_option.clone();
}
error[E0599]: no method named `clone` found for struct `menu_option<'_, &mut i32>` in the current scope
   --> src/lib.rs:24:26
    |
2   | pub struct menu_option<'a, T> {
    | -----------------------------
    | |
    | method `clone` not found for this
    | doesn't satisfy `menu_option<'_, &mut i32>: std::clone::Clone`
...
24  |     let a = leave_option.clone();
    |                          ^^^^^ method not found in `menu_option<'_, &mut i32>`
    |
    = note: the method `clone` exists but the following trait bounds were not satisfied:
            `&mut i32: std::clone::Clone`
            which is required by `menu_option<'_, &mut i32>: std::clone::Clone`
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following trait defines an item `clone`, perhaps you need to implement it:
            candidate #1: `std::clone::Clone`

How do I fix this?

I tried searching online and couldn't find a solution, at least, not one that worked for me (what I found was old Rust, I think)

I don't know what stops the clone because the only use of the T type is a function pointer which I thought was cloneable.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
QurakNerd
  • 652
  • 4
  • 11
  • By the way, idiomatic Rust uses `snake_case` for variables, methods, macros, fields and modules; `UpperCamelCase` for types and enum variants; and `SCREAMING_SNAKE_CASE` for statics and constants. – Shepmaster Jul 28 '20 at 14:33

1 Answers1

3

It works when you implement Clone yourself on menu_option & menu_option_action. By default, since your struct/enum has a type parameter, the macro expansion of the #[derive(Clone)] attribute will restrain the clone implementation for your type to T: Clone.

In your case, not only is this requirement not needed, but it is also very unlikely to be ever respected (e.g. &mut T does not implement Clone). By implementing Clone by hand, you can get rid of the T: Clone requirement and it works!

Note that function pointers implement Copy which is why the menu_option_action::callback(*f) works:

type StateType = i32;

pub struct menu_option<'a, T> {
    pub command: &'a str,
    pub description: &'a str,
    pub action: menu_option_action<'a, T>,
}

impl<'a, T> Clone for menu_option<'a, T> {
    fn clone(&self) -> Self {
        menu_option {
            command: self.command.clone(),
            description: self.description.clone(),
            action: self.action.clone(),
        }
    }
}

pub enum menu_option_action<'a, T> {
    sub_menu(Vec<menu_option<'a, T>>),
    callback(fn(T) -> T),
    leave,
}

impl<'a, T> Clone for menu_option_action<'a, T> {
    fn clone(&self) -> Self {
        match self {
            menu_option_action::sub_menu(sub) => menu_option_action::sub_menu(sub.to_vec()),
            menu_option_action::callback(f) => menu_option_action::callback(*f),
            menu_option_action::leave => menu_option_action::leave,
        }
    }
}

fn main() {
    let mut state: StateType = 88;
    let leave_option: menu_option<&mut StateType> = menu_option {
        command: "l",
        description: "leave",
        action: menu_option_action::leave,
    };

    let a = leave_option.clone();
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
richerarc
  • 250
  • 1
  • 8