Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement fmt::Display on a generically-typed enum in Rust?

I have implemented my linked list using this recursive enum, but now I'd like to implement a custom display format for it

use std::fmt;

#[derive(Debug)]
enum List<A> {
    Empty,
    Cons(A, Box<List<A>>),
}

impl<T> fmt::Display for List<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            List::Empty => write!(f, "()"),
            List::Cons(x, ref xs) => write!(f, "({} {})", x, xs),  
        }
    }
}

Error

error[E0277]: the trait bound `T: std::fmt::Display` is not satisfied
  --> src/main.rs:13:59
   |
13 |             List::Cons(x, ref xs) => write!(f, "({} {})", x, xs),  
   |                                                           ^ the trait `std::fmt::Display` is not implemented for `T`
   |
   = help: consider adding a `where T: std::fmt::Display` bound
   = note: required by `std::fmt::Display::fmt`

Here's the rest of my code if that matters

fn cons<A>(x: A, xs: List<A>) -> List<A> {
    return List::Cons(x, Box::new(xs));
}

fn len<A>(xs: &List<A>) -> i32 {
    match *xs {
        List::Empty => 0,
        List::Cons(_, ref xs) => 1 + len(xs),
    }
}

fn map<A, B>(f: &Fn(&A) -> B, xs: &List<A>) -> List<B> {
    match *xs {
        List::Empty => List::Empty,
        List::Cons(ref x, ref xs) => cons(f(x), map(f, xs)),
    }
}

fn main() {
    let xs = cons(1, cons(2, cons(3, List::Empty)));
    println!("{}", xs);
    println!("{:?}", len(&xs));

    let f = |x: &i32| (*x) * (*x);
    let ys = map(&f, &xs);
    println!("{}", ys);
    println!("{}", List::Empty);
}

Expected output

(1 (2 (3 ())))
3
(1 (4 (9 ())))
()

Really what I'd like to see this, but I have absolutely no idea how I'd go about getting this kind of output using fmt::Result

(1 2 3)
3
(1 4 9) 
()
like image 232
Mulan Avatar asked Mar 30 '17 09:03

Mulan


1 Answers

Solving the compiler error

You are missing a trait bound. That is, you need to tell Rust that T can be displayed:

impl<T: fmt::Display> fmt::Display for List<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            List::Empty => write!(f, "()"),
            List::Cons(ref x, ref xs) => write!(f, "({} {})", x, xs),  
        }
    }
}

Note the trait bound T: fmt::Display. This basically means: if T implements fmt::Display, then List<T> implements fmt::Display as well.

The icing on the cake

I am unsure whether you can get nice formatting with a recursive definition. Furthermore, Rust does not guarantee tail-call optimization, so there would always be the possibility of a stack overflow.

An alternative definition could be:

fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "(")?;
    let mut temp = self;
    while let List::Cons(ref x, ref xs) = *temp {
        write!(f, "{}", x)?;

        // Print trailing whitespace if there are more elements
        if let List::Cons(_, _) = **xs {
            write!(f, " ")?;
        }

        temp = xs;
    }

    write!(f, ")")
}

Note the ? after most write! macro invocations. It basically means: if this write! results in an error, return the error now. Otherwise, continue executing the function.

like image 159
aochagavia Avatar answered Oct 31 '22 12:10

aochagavia