How to get fmt::Display from a struct to display it in the fmt::Display of another struct? - struct

Im not sure how to name this question as I am new to Rust so feel free to suggest edits.
I have two structs. One is a Job struct, which contains a few numbers like how long a job takes, etc.
The other is a JobSequence, which contains a Vec() of Jobs.
I implemented the fmt::Display trait for the Job so that it prints out its three number in this way:
(10, 12, 43)
Now, I would like to implement the fmt::Display trait for the JobSequence struct so that it iterates over each Job in the vector and displays them in this way:
(0, 10, 5)
(30, 10, 5)
(0, 10, 5)
(0, 10, 5)
(0, 10, 5)
I think(?) I should reuse the implemented trait of the Job struct and use it so that it simply, in a way, printouts them as a semi-list. This is my current implementation, but i have a feeling that is sloppy and there is a better way:
pub struct JobSequence {
pub job_sequence: Vec<Job>,
}
impl fmt::Display for JobSequence {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut final_output = String::new();
for i in &self.job_sequence {
final_output.push_str(i.to_string().as_str());
final_output.push_str("\n");
}
write!(f, "{}", final_output)
}
}

You can re-use the the Display impl by passing it directly to write! with the {} format string:
impl fmt::Display for JobSequence {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for i in &self.job_sequence {
writeln!(f, "{}", i)?;
}
Ok(())
}
}
You can read more about the different traits used by the formatting macros in the docs. (A plain {} uses std::fmt::Display.)

Related

Is there a nicer way to implement Display for structs that own collections of things with DIsplay?

I keep finding myself writing Display for structs that hold Vec of some type that implements Display. For example:
use std::fmt::Display;
struct VarTerm {
pub coeffecient: usize,
pub var_name: String,
}
impl Display for VarTerm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}{}", self.coeffecient, self.var_name)
}
}
struct Function {
pub terms: Vec<VarTerm>,
}
impl Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let strings = self
.terms
.iter()
.map(|s| format!("{}", s))
.collect::<Vec<String>>()
.join(" + ");
write!(f, "{}", strings)
}
}
fn main() {
let my_function = Function {
terms: vec![
VarTerm {coeffecient: 2,var_name: "x".to_string(),},
VarTerm {coeffecient: 4,var_name: "y".to_string(),},
VarTerm {coeffecient: 5,var_name: "z".to_string(),},
],
};
println!("All that work to print something: {}", my_function)
}
This looks bulky and ugly to me in a bunch of places - coming from higher-level languages I'm never a fan of the .iter()/.collect() sandwiching (I kind of get why it's needed but it's annoying when 90+ percent of the time I'm just going from Vec to Vec). In this case it's also compounded by the format!() call, which I swear has to be the wrong way to do that.
I'm not sure how much of this is inherent to Rust and how much is me not knowing the right way. I want to get as close as possible to something like:
self.terms.map(toString).join(" + "), which is about what I'd expect in something like Scala.
How close can I get to there? Along the way, is there anything to be done about the aforementioned iter/collect sandwiching in general?
In an eerie coincidence, literally 2 minutes ago I looked at a few methods in the itertools crate. How about this one:
https://docs.rs/itertools/latest/itertools/trait.Itertools.html#method.join
fn join(&mut self, sep: &str) -> String
where
Self::Item: Display
Combine all iterator elements into one String, separated by sep.
Use the Display implementation of each element.
use itertools::Itertools;
assert_eq!(["a", "b", "c"].iter().join(", "), "a, b, c");
assert_eq!([1, 2, 3].iter().join(", "), "1, 2, 3");
EDIT: Additionally, whenever you ask yourself if there was a nicer way to implement a particular trait, especially when the implementation would be somewhat recursive, you should look if there's a derive macro for that trait. Turns out there is, albeit in a separate crate:
https://jeltef.github.io/derive_more/derive_more/display.html
Example:
#[derive(Display)]
#[display(fmt = "({}, {})", x, y)]
struct Point2D {
x: i32,
y: i32,
}
If you find yourself repeating this a lot you might want to move the .into_iter()/.collect() into a specific trait at the cost of generality and composability.
You can also pass ToString::to_string to map.
impl Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let strings = self.terms.map(ToString::to_string).join(" + ");
f.write_str(&strings)
}
}
trait VecMap<'a, T: 'a, U>: IntoIterator<Item = T> {
fn map(self, f: impl FnMut(T) -> U) -> Vec<U>;
}
impl<'a, T: 'a, U> VecMap<'a, &'a T, U> for &'a Vec<T> {
fn map(self, f: impl FnMut(&'a T) -> U) -> Vec<U> {
self.into_iter().map(f).collect()
}
}

Is there a way to limit vector/string length for debug output in Rust?

I need to print arbitrary structures in human-readable form. The problem is they may contain very large vectors, so I would like to limit their length up to N elements appended with ellipsis.
What is a simplest way to achieve that without messing around with procedural macros? Some crates that provide that out-of-the-box?
I'm not aware of any crate doing that, but you can easily create a newtype wrapper:
use std::fmt;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct LimitedVec<T>(pub Vec<T>);
const LIMIT: usize = 5;
impl<T: fmt::Debug> fmt::Debug for LimitedVec<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.0.iter().take(LIMIT)).finish()
}
}
Playground.
Chayim Friedman's answer doesn't include adding the ellipsis.
Tweaking the implementation of fmt::Debug further to support this is not too tricky. I did it by adjusting their code to wrap the iterated values into an enum with a More value. The code can probably be cleaned up a bit.
use core::fmt::Formatter;
use std::fmt;
enum OrMore<T> {
Value(T),
More,
}
impl<T: fmt::Debug> fmt::Debug for OrMore<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
OrMore::Value(t) => fmt::Debug::fmt(t, f),
OrMore::More => write!(f, "..."),
}
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct LimitedVec<T>(pub Vec<T>);
const LIMIT: usize = 5;
impl<T: fmt::Debug> fmt::Debug for LimitedVec<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0.len() <= LIMIT {
f.debug_list().entries(self.0.iter().take(LIMIT)).finish()
} else {
f.debug_list()
.entries(
self.0
.iter()
.take(LIMIT)
.map(OrMore::Value)
.chain(vec![OrMore::More].into_iter()),
)
.finish()
}
}
}
fn main() {
let w = LimitedVec(vec![1, 2, 3, 4, 5]);
let v = LimitedVec(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
println!("{:?}", w); // [1, 2, 3, 4, 5]
println!("{:?}", v); // [1, 2, 3, 4, 5, ...]
}
The code might be shorter if we used an explicit loop using write!(f, ...), rather than using the f.debug_list() approach, but IIRC that doesn't play well with the multi-line debug format {:#?}.

What would be a good signature for a serialization function?

I'm trying to design a simple serialization function:
pub fn serialize(buffer: &[u8]) -> std::result::Result<(), ()> {
Ok(())
}
the above is not ok because the buffer passed to it could not handle the size for serialization.
What would be a good signature for a serialization function? Should I use a vec?
I'm trying to serialize an RTSP message. It's like HTTP. Not much text, it should be simple.
Use a writer, designated by the std::io::Write trait.
pub fn serialize<W: Write>(writer: &mut W) -> Result<()> {
writer.write_all(&[1, 2, 3, 4])?;
Ok(())
}
// or
pub fn serialize(writer: &mut dyn Write) -> Result<()> {
writer.write_all(&[1, 2, 3, 4])?;
Ok(())
}
This makes it generic over many types that can be used as "byte-oriented sinks" like Files, TcpStreams, and normal Vec<u8>s.
fn main() {
let mut buffer = vec![];
serialize(&mut buffer).unwrap();
println!("{:?}", buffer);
}
[1, 2, 3, 4]
See it running on the playground.

How to implement Display on a trait object where the types already implement Display

I have some code which returns a trait object of type MyTrait so that it can return one of several different structs. I would like to implement the Display trait for the trait object so that I can print the object, with the details delegated to the various structs as they each need their own custom formatters.
I can achieve this by including a formatting method as part of the MyTrait definition, and then implementing Display for MyTrait and delegating - like this:
trait MyTrait {
fn is_even(&self) -> bool;
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
impl fmt::Display for MyTrait {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.my_fmt(f)
}
}
However, I already have the Display trait implemented for each of the structs which implement MyTrait. This means I end up with two methods for each struct which do the same thing - the fmt() method to satisfy the Display trait directly on the struct, and the my_fmt() method which is called by the code above. This seems clumsy and repetitive. Is there a simpler way to do it?
Here's a complete example program which illustrates the point. It's a little longer than I would have liked (it's based on the answer to my previous question Calling functions which return different types with shared trait and pass to other functions), but I couldn't think of a simpler way to illustrate the point. Of course, in this toy example the structs and the fmt functions are very simple; in my real application they are more complex.
use std::fmt;
trait MyTrait {
fn is_even(&self) -> bool;
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
struct First {
v: u8,
}
struct Second {
v: Vec<u8>,
}
impl MyTrait for First {
fn is_even(&self) -> bool {
self.v % 2 == 0
}
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v)
}
}
impl MyTrait for Second {
fn is_even(&self) -> bool {
self.v[0] % 2 == 0
}
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v[0])
}
}
fn make1() -> First {
First { v: 5 }
}
fn make2() -> Second {
Second { v: vec![2, 3, 5] }
}
// Implement Display for the structs and for MyTrait
impl fmt::Display for First {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v)
}
}
impl fmt::Display for Second {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v[0])
}
}
impl fmt::Display for MyTrait {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.my_fmt(f)
}
}
fn requires_mytrait<T: MyTrait + ?Sized>(v: &&T) {
println!("{:?}", v.is_even());
}
fn main() {
for i in 0..2 {
let v1;
let v2;
let v = match i {
0 => {
v1 = make1();
println!("> {}", v1); // Demonstrate that Display
// is implemented directly
// on the type.
&v1 as &MyTrait
}
_ => {
v2 = make2();
println!("> {}", v2); // Demonstrate that Display
// is implemented directly
// on the type.
&v2 as &MyTrait
}
};
requires_mytrait(&v);
println!("{}", v); // Here I print the trait object
}
}
Can anyone suggest a simpler, cleaner way to do this?
You can make Display a supertrait of MyTrait.
trait MyTrait: fmt::Display {
fn is_even(&self) -> bool;
}
This will make trait objects of MyTrait be Display. This only works if you expect all implementors of MyTrait to implement Display, but that was also the case in your previous solution.

What is an idiomatic Rust way to format a value to multiple kinds of strings?

I have latitude in degrees in the form of a f64 and I need to convert it into a String. At first I thought about implementing Display, like this:
struct Latitude(f64);
impl fmt::Display for Latitude {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", if self.0 > 0. { "N" } else { "S" }, self.0)
}
}
fn main() {
let lat: f64 = 45.;
println!("{}", Latitude(lat));
}
After that, I have additional requirements. I need convert to one of two representations:
N 70.152351
N 70° 09' 08"
There is also an additional flag; when it is false, I need something like:
- --.------
- --° -' -"
The most simple way to implement this will be:
fn format_lat(degree: f64, use_min_sec_variant: bool, is_valid: bool) -> String;
However, I don't see any free functions in the Rust standard library.
Maybe I should use struct Latitude(f64) and implement a to_string method? Or maybe I should implement some other trait?
You can create wrapper types with different types of formatting and return them as a method:
struct Latitude(f64);
struct MinutesSeconds(f64);
impl Latitude {
fn minutes_seconds(&self) -> MinutesSeconds {
MinutesSeconds(self.0)
}
}
impl fmt::Display for MinutesSeconds {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "latitude in a different format")
}
}
This is the same idea as Path::display.
As for the valid / invalid concept, it sounds like you really want a struct Latitude(Option<f64>) and then provide None when it is invalid.
Basically you're free to do what you want. Without more context on your goals, any method seems good enough for me.
Personally I'd do it as:
struct Latitude(f64);
impl Latitutde {
pub fn format_as_x(&self, f: &mut fmt::Formatter) -> fmt::Result<(), Error> {}
pub fn format_as_y(&self, f: &mut fmt::Formatter) -> fmt::Result<(), Error> {}
}
plus a Display trait + to_format_x() -> String convenience functions.
format_as_y are IMO best method since they handle errors, can take any formatter and don't need to allocate a String to return.
By the way, taking bool arguments is usually an anti-pattern: boolean trap.
You can impl Latitude if you need to pass arguments or impl ToString for Latitude if you don't:
use std::string::ToString;
struct Latitude(f64);
// If you need parameterisation on your conversion function
impl Latitude {
// You'd probably use a better-suited name
pub fn to_string_parameterised(&self, use_min_sec_variant: bool) -> String {
// Conversion code
format!("{} {}", self.0, use_min_sec_variant)
}
}
// If you don't need parameterisation and want to play nicely with 100% of all code elsewhere
impl ToString for Latitude {
fn to_string(&self) -> String {
// Conversion code
format!("{}", self.0)
}
}
fn main() {
let lat = Latitude(45.0);
println!("{}", lat.to_string_parameterised(false));
println!("{}", lat.to_string());
}

Resources