如何在Rust中实现对一般类型枚举的fmt::Display?

0lvr5msh  于 2022-11-12  发布在  其他
关注(0)|答案(2)|浏览(168)

我已经使用这个递归枚举实现了我的链表,但是现在我想为它实现一个自定义的显示格式

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[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`

以下是我剩下的代码

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);
}

预期输出

(1 (2 (3 ())))
3
(1 (4 (9 ())))
()
  • 真的 * 我很想看到这一点,但我完全不知道如何使用fmt::Result来获得这种输出
(1 2 3)
3
(1 4 9) 
()
14ifxucb

14ifxucb1#

解决编译错误

您缺少一个特征边界,也就是说,您需要告诉Rust可以显示T

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),  
        }
    }
}

注意特征绑定的T: fmt::Display,这基本上意味着:如果T实现了fmt::Display,则List<T>也实现了fmt::Display
蛋糕上的糖衣
我不确定你是否能用递归定义得到很好的格式。另外,Rust不保证尾部调用优化,所以总是有堆栈溢出的可能性。
另一种定义可以是:

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, ")")
}

请注意大多数write!宏调用之后的?。它的基本含义是:如果这个write!导致了一个错误,现在就返回这个错误。否则,继续执行这个函数。

r3i60tvu

r3i60tvu2#

作为对其他答案的补充,我想提到格式对齐。例如,如果你想让用户能够在列视图中对齐输出,请确保选中f.align()

enum Gender{
    Male,
    Female
}
impl fmt::Display for Gender {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match f.align() {
            None => match self {
                Gender::Male => write!(f, "male"),
                Gender::Female => write!(f, "female"),
            },
            Some(_) => f.pad(&self.to_string()), // calls this function recursively but without alignments
        }
    }
}

fn main(){
    let man = Gender::Male;
    let woman = Gender::Female;
    let width = 10;
    println!("{0:<width$} and {1:<width$}", man, woman);
}

相关问题