[Types, Generic Functions] Pizza with Any Toppings


#1

I’m working through The Little MLer, first time doing so using Rust. I have the following types:

#[derive(Debug,Eq,PartialEq)]
pub enum Pizza<T> {
    Bottom,
    Topping(T,Box<Pizza<T>>),
}

#[derive(Debug,Eq,PartialEq)]
pub enum Fish {
    Anchovy,
    Lox,
    Tuna,
}

I’ve written a function rem_anchovy for removing anchovies from a fish-topped pizza:

pub fn rem_anchovy(pizza: Pizza<Fish>) -> Pizza<Fish> {
    use Pizza::*;
    match pizza {
        Bottom => Bottom,
        Topping(Fish::Anchovy,p) => rem_anchovy(*p),
        Topping(f,p) => Topping(f, Box::new(rem_anchovy(*p))),
    }
}

How can I make rem_anchovy generic and allow removing Fish::Anchovy from an arbitrary Pizza<T>? My initial attempt does not compile:

pub fn rem_anchovy_any<T>(pizza: Pizza<T>) -> Pizza<T> {
    use chap05::Pizza::*;
    match pizza {
        Bottom => Bottom,
        Topping(Fish::Anchovy,p) => rem_anchovy_any(*p),
        Topping(x,p) => Topping(x, Box::new(rem_anchovy_any(*p))),
    }
}

Produces the following error:

src/chap05.rs:36:17: 36:30 error: mismatched types:
 expected `T`,
    found `chap05::Fish`
(expected type parameter,
    found enum `chap05::Fish`) [E0308]
src/chap05.rs:36         Topping(Fish::Anchovy,p) => rem_anchovy_any(*p),

I feel like I’m forgetting/missing something essential to how Rust’s type system is put together, so I’d appreciate any input folks have (nothing is too basic or too advanced, be as detailed as desired). Thanks in advance.


#2

I think you can’t do this. T can be arbitrary type (like Pizza<f32>), and you can’t compare arbitrary types in Rust.

To find a problem formulation which can be solved in Rust, I’d advise to read about static and dynamic dispatch flavors in Rust.

This docs can be helpful:

https://doc.rust-lang.org/book/traits.html
https://doc.rust-lang.org/book/trait-objects.html
https://doc.rust-lang.org/std/any/


#3

I don’t know if the following counts as cheating

#[derive(Debug, Eq, PartialEq)]
pub enum Fish {
    Anchovy,
    Lox,
    Tuna,
}

#[derive(Debug,Eq,PartialEq)]
pub struct Pizza {
    toppings: Vec<Fish>
}


pub fn rem_anchovy_any(pizza: Pizza) -> Pizza {
    Pizza{ 
        toppings: pizza.toppings.into_iter().filter(|t| *t != Fish::Anchovy).collect() 
    }
}