如何打印一个Vec?

154
我尝试了以下代码:

fn main() {
    let v2 = vec![1; 10];
    println!("{}", v2);
}

但是编译器报错了:

error[E0277]: `std::vec::Vec<{integer}>` doesn't implement `std::fmt::Display`
 --> src/main.rs:3:20
  |
3 |     println!("{}", v2);
  |                    ^^ `std::vec::Vec<{integer}>` cannot be formatted with the default formatter
  |
  = help: the trait `std::fmt::Display` is not implemented for `std::vec::Vec<{integer}>`
  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
  = note: required by `std::fmt::Display::fmt`

有人为 Vec<T> 实现了这个 trait 吗?
7个回答

214
let v2 = vec![1; 10];
println!("{:?}", v2);

{}用于字符串和其他可以直接显示给用户的值。对于向用户展示向量的方法并没有唯一的方式。

{:?}格式化程序可用于调试向量,它将显示如下:

[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

Display 是提供 {} 方法后面的特征,而 Debug 则是提供 {:?} 方法的特征。


1
这是问题标题的正确答案。它也适用于数组,不仅限于 vec! - Rutrus
1
有没有一种方法可以更美观地格式化向量,使其不只是单行打印? - Anatoly
7
你可以使用备用格式说明符 {:#?} 来使输出跨越多行。 - kmdreko

73

有人为 Vec<T> 实现了这个特质吗?

没有。

令人惊讶的是,这是一个可以证明的正确答案;这在证明不存在某些事情时通常很难或不可能。那么我们怎么能如此确定呢?

Rust 有非常严格的协同规则,impl Trait for Struct 只能在:

  • Trait 相同的 crate 中
  • Struct 相同的 crate 中

而不是其他任何地方;让我们试试

impl<T> std::fmt::Display for Vec<T> {
    fn fmt(&self, _: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        Ok(())
    }
}

产生:

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
 --> src/main.rs:1:1
  |
1 | impl<T> std::fmt::Display for Vec<T> {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type parameter `T` must be used as the type parameter for some local type
  |
  = note: only traits defined in the current crate can be implemented for a type parameter

此外,要使用特征,需要将其置于作用域中(因此,您需要链接到其箱),这意味着:
- 您同时链接到 `Display` 的箱和 `Vec` 的箱 - 都没有为 `Vec` 实现 `Display`
因此,我们得出结论:没有人为 `Vec` 实现 `Display`。
作为一种解决方法,正如Manishearth所指出的那样,您可以使用Debug特性,该特性可以通过"{:?}"作为格式说明符来调用。

错误 E0210 是指仅允许在 collections/vec.rs 文件中实现 trait Display 吗? - highfly22
1
@highfly22:我的理解是它应该在同一个 crate 中,但不一定是同一个文件。 - Matthieu M.
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - BitTickler
3
不。解决方案是声明一个新类型:struct Mine(That3rdPartyType);,然后实现 Mine 类型的 Display - Matthieu M.
精彩的解释! - EdwardG

25

如果您知道向量包含的元素类型,您可以创建一个以向量为参数的结构体,并为该结构体实现Display

use std::fmt::{Display, Formatter, Error};

struct NumVec(Vec<u32>);

impl Display for NumVec {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        let mut comma_separated = String::new();

        for num in &self.0[0..self.0.len() - 1] {
            comma_separated.push_str(&num.to_string());
            comma_separated.push_str(", ");
        }

        comma_separated.push_str(&self.0[self.0.len() - 1].to_string());
        write!(f, "{}", comma_separated)
    }
}

fn main() {
    let numbers = NumVec(vec![1; 10]);
    println!("{}", numbers);
}

2
没有要求您必须知道元素的*确切类型;您可以使用通用类型并允许任何实现Display的类型 - Shepmaster
1
很不幸,shepmasters的gist已经无法使用了。我从这里的示例中重新构建了一个:https://play.rust-lang.org/?gist=b28b96a28d081601e2e09b1fc26f8ade&version=stable&mode=debug&edition=2015 - zzeroo

13

以下是一个一行代码,它也适用于您的情况:

println!("[{}]", v2.iter().fold(String::new(), |acc, &num| acc + &num.to_string() + ", "));

这里有一个可运行的示例。


在我的情况中,我从一个函数调用中收到了一个Vec<&str>。我不想将函数签名更改为自定义类型(对于该类型我可以实现Display trait)。

对于我的个别情况,我能够将我的Vec的显示转换为单行代码,并直接使用println!(),如下所示:

println!("{}", myStrVec.iter().fold(String::new(), |acc, &arg| acc + arg));

lambda 可以适用于不同的数据类型,或者更简洁地实现 Display trait。


8

从Rust 1.58开始,有一种略微更简明的方法可以打印向量(或任何其他变量)。这使您可以将要打印的变量放在大括号内,而不需要将其放在末尾。对于打印向量所需的调试格式化,您可以在大括号中添加:?,像这样:

fn main() {
    let v2 = vec![1; 10];
    println!("{v2:?}");
}

5
有时候你不想使用像被接受的答案那样的东西。
let v2 = vec![1; 10];
println!("{:?}", v2);

因为您希望每个元素都使用其Display特性而不是Debug特性进行显示;但是,正如已经注意到的那样,由于Rust的一致性规则,您不能在Vec上实现Display。因此,您可以使用以下函数实现更通用的解决方案,而不是使用具有Display特性的包装结构体:

use std::fmt;

pub fn iterable_to_str<I, D>(iterable: I) -> String
where
    I: IntoIterator<Item = D>,
    D: fmt::Display,
{
    let mut iterator = iterable.into_iter();

    let head = match iterator.next() {
        None => return String::from("[]"),
        Some(x) => format!("[{}", x),
    };
    let body = iterator.fold(head, |a, v| format!("{}, {}", a, v));
    format!("{}]", body)
}

这种方法不需要将向量包装在结构体中。只要 it 实现了 IntoIterator ,元素类型实现了 Display ,你就可以调用:

println!("{}", iterable_to_str(it));

这是一个很好的回答。我喜欢人们建议使用调试打印而不是说你不能这样做。 - Tanveer Badar

-1

不收集先前的内容,逐项编写向量的内容有什么理由不这样做吗?*)

use std::fmt::{Display, Formatter, Error};

struct NumVec(Vec<u32>);

impl Display for NumVec {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        let v = &self.0;
        if v.len() == 0 {
            return Ok(());
        }
        for num in &v[0..v.len() - 1] {
            if let Err(e) = write!(f, "{}, ", &num.to_string()) {
                return Err(e);
            }
        }
        write!(f, "{}", &v[v.len() - 1])
    }
}

fn main() {
    let numbers = NumVec(vec![1; 10]);
    println!("{}", numbers);
}

*) 没有。

因为我们想要显示一些东西,所以肯定实现了 Display 特质。因此这是正确的 Rust,因为文档关于 ToString trait 的说明如下:

"对于任何实现了 Display 特质的类型,该特质会自动实现。因此,不应直接实现 ToString:应该直接实现 Display,然后您就可以免费获得 ToString 实现。"

特别是在空间有限的微控制器上,我肯定会选择这种解决方案并立即编写。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接