I have a generic function with a trait bound on Display
fn display_all<T>(items: BTreeMap<T, i32>)
where T: std::fmt::Display
Now I'd like to pass Option and non-Option types to it. But of course, Option doesn't implement Display, so this doesn't work.
let mut non_opt = BTreeMap::new();
non_opt.insert("One", 1);
let mut opt = BTreeMap::new();
opt.insert(Some("Two".to_string()), 2);
display_all(non_opt);
display_all(opt);
Ideally, a default value should be used if a value is none. I'm unsure wheter the default value should be provided by the caller or the callee, but I'm currently leaning towards the latter. I've tried a wrapper trait, but this also doesn't work: "conflicting implementations of trait DisplayOption for type std::option::Option<_>"
If I remove the implementation for T the code works. It's just that now I need to implement Display and DisplayOption for the types I want to pass to the function, which creates annoying boilerplate. So I'd like to know if there is a better solution.
I don't know the use-case for this, but using Option for key in BTreeMap seems kind of wasteful, since key must be unique, so in this case there will be only one key/value pair with default value for Option as key. So user could just use non-option map and call inserts like this: non_opt.insert(some_option.unwrap_or_default(), value);
The use case for this is I'm getting data from the GitLab API and then want to group it by a certain criteria, i.e. Labels on an issue. But sometimes a issue doesn't have a label and that is where the key would be None. The i32 is just a simplified example, in my application the BTreeMap is actually a BTreeMap<T, Vec<_>>.
Regarding implementing Display trait for structs that do not implement it and are defined in different modules, i would also be interested if there is some nice trick for it.
What i currently do is. I create an easy to use wrapper struct and use it in calls. It is inconvenient, but i have not come up with better solution for now. The code is something along the lines:
struct DisplayWrapper<'a>(pub &'a BTreeMap<_, _>);
impl Display for DisplayWrapper {}
// Then in code:
println!("{}", DisplayWrapper(&your_tree));
Yes, if I remove it the code works. It's just that now I need to implement Display and DisplayOption for the types I want to pass to the function, which creates annoying boilerplate. So I'd like to know if there is a better solution.
The error with your wrapper trait is that the two impls
impl<T: Display> DisplayOption for T, when T = Option<Foo>, and
impl<T: Display> DisplayOption for Option<T>, when T = Foo
(for some Foo: Display) collide if Option<Foo>: Display (required by the trait bound in the first impl). Of course, this impl doesn't exist, but the coherence checker enforces std's right to add it in a future semver-compatible update.
To allow accepting both types, you'll have to "merge" Option<T> and T differently. The simple way is to just wrap the Ts in Some, but that would require reallocating the entire BTreeMap.
Instead, you can convert Option<T> to a wrapper type that's Display. Make display_all<T: Display> accept an IntoIterator<Item = (T, &i32)>.[1] Then use (edited so it actually works)
let non_optional: BTreeMap<String, i32>;
// This call uses T = &String
display_all(&non_optional);
use std::fmt::{self, Display};
struct DisplayOption<T>(Option<T>);
impl<T: Display> Display for DisplayOption<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(x) = &self.0 {
x.fmt(f)
} else {
write!(f, "None")
}
}
}
let optional: BTreeMap<Option<String>, i32>;
// This call uses T = DisplayOption<&String>
display_all(optional.iter().map(|(k, v)| (DisplayOption(k.as_ref()), v)));
You can also use display_all<T: Display>(impl IntoIterator<Item = (Option<T>, i32)>), but you'll need an Iterator::map on both the optional and non-optional uses, and the responsibility for determining the display value of None falls on display_all.
Not sure if I'm doing this right, but I get a compiler error
error[E0308]: mismatched types
--> src/main.rs:8:20
|
8 | if let Some(x) = self {
| ^^^^^^^ ---- this expression has type `&DisplayOption<T>`
| |
| expected `DisplayOption<T>`, found `Option<_>`
|
= note: expected struct `DisplayOption<T>`
found enum `Option<_>`
error[E0599]: the method `fmt` exists for mutable reference `&mut Formatter<'_>`, but its trait bounds were not satisfied
--> src/main.rs:9:19
|
9 | f.fmt(x)
| ^^^ method cannot be called on `&mut Formatter<'_>` due to unsatisfied trait bounds
|
::: /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:566:1
|
566 | pub struct Formatter<'a> {
| ------------------------ doesn't satisfy `Formatter<'_>: std::fmt::Display`
|
= note: the following trait bounds were not satisfied:
`Formatter<'_>: std::fmt::Display`
which is required by `&mut Formatter<'_>: std::fmt::Display`
= help: items from traits can only be used if the trait is in scope
help: trait `Pointer` which provides `fmt` is implemented but not in scope; perhaps you want to import it
|
1 + use std::fmt::Pointer;
|