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

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

Related

How to display value contained in Result or a generic message if Result is Err

I would like to print T if Ok(T) or a generic message and do this as part of the same println!() statement.
My Current solution which works is:
fn main() {
let x: std::io::Result<i32> = Ok(54);
println!("hello {} ", x.map(|i| i.to_string()).unwrap_or("Bad".to_string()));
}
Is there a simpler and more efficient way that would leverage the Display trait instead of needing to convert to string inside map and unwrap_or?
You can cast each of the Result values as &dyn Display
let display = match &x {
Ok(i) => i as &dyn Display,
Err(_) => &"error" as &dyn Display,
};
// or with map
let display = x
.as_ref()
.map(|x| x as &dyn Display)
.unwrap_or_else(|_| &"error" as &dyn Display);
println!(
"hello {} ",
display
)
Both can be in-lined into the println!, I have just separated them for readability.
While the accepted solution works and answers your question exactly, I don't think it's the most readable (or performant). (Correction: it's in fact faster.) A trait object feels like overkill to me. Consider using the same approach as yours, but instead, merge the map() and unwrap_or() into map_or() (or map_or_else(), to avoid an unnecessary heap allocation).
use std::io;
fn main() {
let x: io::Result<i32> = Ok(54);
println!("Hello, {}.", x.map_or_else(|_err| "Bad".into(), |i| i.to_string()));
}
Run this snippet on Rust Playground.
If you're not going to use the error value, you can make the code a touch more concise by renaming _err to _.
On the other hand, if you're going to be doing this often and in many places, you can create a wrapper type and implement your Display logic for that.
use std::fmt::{self, Display};
use std::io;
fn main() {
let x: io::Result<i32> = Ok(54);
println!("Hello, {}.", PrintableResult(&x));
}
struct PrintableResult<'a, T: Display, E>(&'a Result<T, E>);
impl<'a, T: Display, E> Display for PrintableResult<'a, T, E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
self.0
.as_ref()
.map_or_else(|_| "Bad".into(), |val| val.to_string())
)
}
}
Run this snippet on Rust Playground.
If you don't mind PrintableResult taking ownership of your Result value, you can drop the borrow and the lifetime specifier with it.
If you'd like, you can also implement a to_printable() extension method for Result (via a trait).
// ...
fn main() {
let x: io::Result<i32> = Ok(54);
println!("Hello, {}.", x.to_printable());
let y: Result<i32, &'static str> = Err("Something went wrong.");
println!("Hello, {}.", y.to_printable());
}
// ...
trait ToPrintable<T: Display, E> {
fn to_printable(&self) -> PrintableResult<T, E>;
}
impl<T: Display, E> ToPrintable<T, E> for Result<T, E> {
fn to_printable(&self) -> PrintableResult<T, E> {
PrintableResult(&self)
}
}
Run this snippet on Rust Playground.

Efficient Rust collector of Results holding Vecs

I'm learning Rust, and I've come upon the following pattern which collapses an iterator of Result<Vec<_>, _>s to a single big Vec<_>, failing if any of the results from the iterator failed:
fn accumulate<T, E>(it: impl Iterator<Item = Result<Vec<T>, E>>) -> Result<Vec<T>, E> {
let mut result = Vec::new();
for mut ts in it {
result.append(&mut ts?)
}
Ok(result)
}
I assume a very short "functional-style" version of this function can be written, and I'm struggling to find it. Morally, I'd like to do something like
it.map(|v| v?.into_iter()).flatten().collect()
but this doesn't typecheck. By running small examples, I think the point of the flatten there is to silently drop error results, but I'd instead like to somehow "map the flatten under the Results". I know also that in general you couldn't collect, say, an iterator of type
impl Iterator<Item = Result<impl Iterator<Item = T>, Error>>
into an iterator
Result<impl Iterator<Item = impl Iterator<Item = T>>, Error>
since you need to have actually done all of the computations in the outer iterator in order to know the final result. Nonetheless, it seems that you can make this work in this special case, when you want to .flatten() and then .collect() right after.
Finally, I can see that that collect() gives me a way to build a vector of vectors from it, and then I could flatten this vector into the single big vector I want. But this has many needless memory allocations.
Can the standard library help you do this in an efficient, Rust-ic way?
I think I would start with try_fold, as it can deal with Result and stop on Err:
fn acc2<T, E>(mut it: impl Iterator<Item = Result<Vec<T>, E>>) -> Result<Vec<T>, E> {
it.try_fold(
Vec::new(),
|mut vec, res_ts: Result<Vec<_>, E>| {
res_ts.map(move |mut ts| { // map preserves Err
// In case of Ok(...), append to already found elements
vec.append(&mut ts);
vec
})
}
)
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f6f738ddedecda1875df283f221dbfdc
It turns out, Itertools already has fold_results that should do what you want:
fn acc3<T, E>(mut it: impl Iterator<Item = Result<Vec<T>, E>>) -> Result<Vec<T>, E> {
it.fold_results(
Vec::new(),
|mut vec, mut ts| {
vec.append(&mut ts);
vec
}
)
}
To achieve this only using iterator methods:
use std::iter::{self, Iterator};
pub fn accumulate<T, E>(it: impl Iterator<Item = Result<Vec<T>, E>>) -> Result<Vec<T>, E> {
it.flat_map(|v| {
v.map_or_else(
|e| Iter::A(iter::once(Err(e))),
|t| Iter::B(t.into_iter().map(Ok)),
)
})
.collect()
}
// Utility enum that can be generated by the #[auto_enum] derive macro
enum Iter<T, A: Iterator<Item = T>, B: Iterator<Item = T>> {
A(A),
B(B),
}
impl<T, A: Iterator<Item = T>, B: Iterator<Item = T>> Iterator for Iter<T, A, B> {
type Item = T;
fn next(&mut self) -> Option<T> {
match self {
Self::A(a) => a.next(),
Self::B(b) => b.next(),
}
}
}
This uses flat_map to yield either an iterator of Oks or an iterator of an Err for each entry.
This is semantically equivalent to your control flow code using for loop.
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=68558e27900940476e443d670a120e91
See auto_enums for deriving an enum delegating Iterator variants.
Alternatively, you can use either::Either in place of Iter, which has the same implementation for two items:
https://docs.rs/either/1.5.3/either/enum.Either.html#impl-Iterator

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

How to implement a trait for a parameterized trait

I have a design issue, when using something like :
trait MyTrait<K: OtherTrait> { ... }
impl<K: OtherTrait, M: MyTrait<K>> AnyTrait for M { ... }
I cannot implement trait for this trait due to E207 error ("the type parameter K is not constrained by the impl trait, self type, or predicates").
Finding no way to get rid of this error, I apply this not-so-good-looking workaround (verbose and struct with no intrinsic value):
use std::fmt;
use std::marker::PhantomData;
pub trait MyTrait<K: fmt::Display> {
fn get_some_k(&self) -> Option<K>;
}
/* // This is my target impl but results in E207 due to K not constrained
impl<K: fmt::Display, S: MyTrait<K>> fmt::Display for S {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.get_some_k().unwrap())
}
} */
pub struct Ugly<'a, K: fmt::Display, S: 'a + MyTrait<K>>(&'a S, PhantomData<K>);
impl<'a, K: fmt::Display, S: MyTrait<K>> fmt::Display for Ugly<'a, K, S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0.get_some_k().unwrap())
}
}
fn main() { }
I think there should be some nicer way to implement a trait for this kind of parameterized trait.
I did not find good example in std (for instance no Display implementation in traits with associated type like Iterator)?
Here’s an implementation using associated types (which means that you can only implement MyTrait for one K per type):
use std::fmt;
pub trait MyTrait {
type K: fmt::Display;
fn get_some_k(&self) -> Option<Self::K>;
}
impl<S: MyTrait> fmt::Display for S {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.get_some_k().unwrap())
}
}
fn main() { }
However, when clarified like this it becomes clear that this approach won’t work either, because you’re implementing Display for all types that implement MyTrait—types that could have their own Display implementation. This is forbidden, and so you get E0210:
error: type parameter S must be used as the type parameter for some local type (e.g. MyStruct<T>); only traits defined in the current crate can be implemented for a type parameter [E0210]
Wrapping it in something—like your Ugly did—is the only way to allow such an implementation. Or implement a trait in your own crate rather than one in someone else’s (like Display is).

Resources