Wie drucke ich einen Vec?

73

Ich habe den folgenden Code ausprobiert:

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

Aber der Compiler beschwert sich:

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`

Implementiert jemand dieses Merkmal für Vec<T>?

highfly22
quelle

Antworten:

58

Implementiert jemand dieses Merkmal für Vec<T>?

Nein.

Und überraschenderweise ist dies eine nachweislich richtige Antwort. Das ist selten, da es normalerweise schwierig oder unmöglich ist, das Fehlen von Dingen zu beweisen. Wie können wir also so sicher sein?

Rust hat sehr strenge Kohärenzregeln, die impl Trait for Structnur gemacht werden können:

  • entweder in der gleichen Kiste wie Trait
  • oder in der gleichen Kiste wie Struct

und nirgendwo anders; Lass es uns versuchen :

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

Ausbeuten:

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

Um ein Merkmal zu verwenden, muss es außerdem im Geltungsbereich sein (und daher müssen Sie mit seiner Kiste verknüpft sein). Dies bedeutet, dass:

  • Sie sind sowohl mit der Kiste Displayals auch mit der Kiste von verbundenVec
  • weder implementieren DisplayfürVec

und führt uns daher zu dem Schluss, dass niemand Displayfür implementiert Vec.


Um dies zu umgehen, können Sie, wie von Manishearth angegeben, das DebugMerkmal verwenden, das über "{:?}"als Formatbezeichner aufgerufen werden kann.

Matthieu M.
quelle
Bedeutet der Fehler E0210, dass die Anzeige von Merkmalen nur in den Dateisammlungen / vec.rs implementiert werden darf?
highfly22
1
@ highfly22: Mein Verständnis ist, dass es in derselben Kiste sein sollte, nicht unbedingt in derselben Datei.
Matthieu M.
1
Was? Ich kann also nicht ad hoc ein Display-Merkmal in das Modul einfügen, in dem ich es für einen Typ in einer anderen Kiste benötige.
BitTickler
3
@BitTickler: Nein. Die Lösung besteht darin, einen neuen Typ zu deklarieren: struct Mine(That3rdPartyType);und dann impl Display for Mine.
Matthieu M.
4
Hier ist ein "Newtype" -Beispiel: play.rust-lang.org/…
zzeroo
87
let v2 = vec![1; 10];
println!("{:?}", v2);

{}ist für Zeichenfolgen und andere Werte, die dem Benutzer direkt angezeigt werden können. Es gibt keine einzige Möglichkeit, einem Benutzer einen Vektor anzuzeigen.

Der {:?}Formatierer kann zum Debuggen verwendet werden und sieht folgendermaßen aus:

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

Displayist das Merkmal, das die Methode dahinter liefert {}, und Debugfür ist{:?}

Manishearth
quelle
22

Wenn Sie den Typ der Elemente kennen, die der Vektor enthält, können Sie eine Struktur erstellen, die den Vektor als Argument verwendet, und Displaydiese Struktur implementieren .

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);
}
der Böse
quelle
2
Es ist nicht erforderlich, dass Sie den genauen Typ der Elemente kennen. Sie können ein Generikum verwenden und jeden Typ zulassen, der implementiert wirdDisplay .
Shepmaster
1
Leider funktioniert Shepmasters Gist nicht mehr. Ich habe eines aus den Beispielen hier neu erstellt: play.rust-lang.org/…
zzeroo
6

Hier ist ein Einzeiler, der auch für Sie funktionieren sollte:

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

Hier ist ein lauffähiges Beispiel.


In meinem Fall erhielt ich einen Vec<&str>von einem Funktionsaufruf. Ich wollte die Funktionssignatur nicht in einen benutzerdefinierten Typ ändern (für den ich das DisplayMerkmal implementieren konnte ).

Für meinen Einzelfall konnte ich das Display meines Vecin einen Einzeiler verwandeln, den ich println!()direkt wie folgt verwendete:

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

(Das Lambda kann für die Verwendung mit verschiedenen Datentypen oder für Displaypräzisere Implementierungen von Merkmalen angepasst werden .)

U007D
quelle
-1
Does anyone implement this trait for Vec<T>?

Ja, so habe ich es gemacht

fn main() {
    let a = vec![10,12,13];
    println!("{:?}",display(a));
}


use std::fmt::Debug;
fn display<T: Debug>(a: Vec<T>) -> Vec<T> {
    let mut new_vec : Vec<T> = Vec::new();
    
    for i in a {
        new_vec.push(i);
    }
    return new_vec
} 
Taimoor
quelle