Why does the compiler claim that a generic doesn't implement `Display` even though it should?
I'm building a library that implements string joins; that is, printing all the elements of a container separated by a separator. My basic design looks like this:
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Join<Container, Sep> {
container: Container,
sep: Sep,
}
impl<Container, Sep> fmt::Display for Join<Container, Sep>
where
for<'a> &'a Container: IntoIterator,
for<'a> <&'a Container as IntoIterator>::Item: fmt::Display,
Sep: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut iter = self.container.into_iter();
match iter.next() {
None => Ok(()),
Some(first) => {
first.fmt(f)?;
iter.try_for_each(move |element| {
self.sep.fmt(f)?;
element.fmt(f)
})
}
}
}
}
This trait implementation compiles without complaint. Notice the bound on &'a C: IntoIterator
. Many containers implement IntoIterator
for a reference to themselves, to allow for iterating over references to the contained items (for instance, Vec
implements it here).
However, when I actually try to use my Join
struct, I get an unsatisfied trait bound:
fn main() {
let data = vec!["Hello", "World"];
let join = Join {
container: data,
sep: ", ",
};
println!("{}", join);
}
This code produces a compilation error:
error[E0277]: `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item` doesn't implement `std::fmt::Display`
--> src/main.rs:38:20
|
38 | println!("{}", join);
| ^^^^ `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item` cannot be formatted with the default formatter
|
= help: the trait `for<'a> std::fmt::Display` is not implemented for `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required because of the requirements on the impl of `std::fmt::Display` for `Join<std::vec::Vec<&str>, &str>`
= note: required by `std::fmt::Display::fmt`
The key line seems to be this:
the trait `for<'a> std::fmt::Display` is not implemented for `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item`
Unfortunately, the compiler doesn't actually tell me what the Item
type is, but based on my reading of the docs, it appears to be &T
, which in this case means &&str
.
Why doesn't the compiler think that &&str
implements Display
? I've tried this with many other types, like usize
and String
, and none of them work; they all fail with the same error. I know that these reference type don't directly implement Display
, but the implementation should be picked up automatically through deref coercion, right?
rust formatting traits
|
show 1 more comment
I'm building a library that implements string joins; that is, printing all the elements of a container separated by a separator. My basic design looks like this:
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Join<Container, Sep> {
container: Container,
sep: Sep,
}
impl<Container, Sep> fmt::Display for Join<Container, Sep>
where
for<'a> &'a Container: IntoIterator,
for<'a> <&'a Container as IntoIterator>::Item: fmt::Display,
Sep: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut iter = self.container.into_iter();
match iter.next() {
None => Ok(()),
Some(first) => {
first.fmt(f)?;
iter.try_for_each(move |element| {
self.sep.fmt(f)?;
element.fmt(f)
})
}
}
}
}
This trait implementation compiles without complaint. Notice the bound on &'a C: IntoIterator
. Many containers implement IntoIterator
for a reference to themselves, to allow for iterating over references to the contained items (for instance, Vec
implements it here).
However, when I actually try to use my Join
struct, I get an unsatisfied trait bound:
fn main() {
let data = vec!["Hello", "World"];
let join = Join {
container: data,
sep: ", ",
};
println!("{}", join);
}
This code produces a compilation error:
error[E0277]: `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item` doesn't implement `std::fmt::Display`
--> src/main.rs:38:20
|
38 | println!("{}", join);
| ^^^^ `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item` cannot be formatted with the default formatter
|
= help: the trait `for<'a> std::fmt::Display` is not implemented for `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required because of the requirements on the impl of `std::fmt::Display` for `Join<std::vec::Vec<&str>, &str>`
= note: required by `std::fmt::Display::fmt`
The key line seems to be this:
the trait `for<'a> std::fmt::Display` is not implemented for `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item`
Unfortunately, the compiler doesn't actually tell me what the Item
type is, but based on my reading of the docs, it appears to be &T
, which in this case means &&str
.
Why doesn't the compiler think that &&str
implements Display
? I've tried this with many other types, like usize
and String
, and none of them work; they all fail with the same error. I know that these reference type don't directly implement Display
, but the implementation should be picked up automatically through deref coercion, right?
rust formatting traits
It looks like the compiler is treatingfor<'a> Display
as different fromDisplay
. This might be a bug in the compiler.
– Francis Gagné
Nov 18 '18 at 20:48
1
This indeed looks like a shortcoming in the compiler to me. You are right that the item type is&&str
, as you can easily verify by trying to compilelet _: <&Vec<&str> as IntoIterator>::Item = ();
. And&&str
definitely does implementDisplay
, as can be verified by adding<&&str as fmt::Display>::fmt(&&"hello", f)?;
to the body offmt()
. So this should definitely work. My guess is that<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item
isn't correctly normalized to&'a &str
for some reason.
– Sven Marnach
Nov 18 '18 at 20:52
1
See also github.com/rust-lang/rust/issues/24159 and the issues linked there. Often this can be worked around by adding an additional type parameter for the item type, and forcing it to be the actual item type by changing the trait boundIntoIterator
toIntoIterator<Item = T>
, whereT
is the new type parameter. This doesn't work int his case, though, due to the HRTBs.
– Sven Marnach
Nov 18 '18 at 21:19
You actually can do that in this case; I was able to try<T: Display> ... where &'a C: IntoIterator<Item=T>
. However, I got a different and somehow even less useful error message about unfulfilled lifetime bounds.
– Lucretiel
Nov 18 '18 at 22:48
1
@Lucretiel The reason this does not work is because of the lifetime ofItem
. In your example, the item type is&&str
, but annoted with the lifetime of the HRTB, it would have to be&'a &str
for all lifetimes'a
, which of course is impossible. That's what I meant in my previous comment.
– Sven Marnach
Nov 19 '18 at 7:38
|
show 1 more comment
I'm building a library that implements string joins; that is, printing all the elements of a container separated by a separator. My basic design looks like this:
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Join<Container, Sep> {
container: Container,
sep: Sep,
}
impl<Container, Sep> fmt::Display for Join<Container, Sep>
where
for<'a> &'a Container: IntoIterator,
for<'a> <&'a Container as IntoIterator>::Item: fmt::Display,
Sep: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut iter = self.container.into_iter();
match iter.next() {
None => Ok(()),
Some(first) => {
first.fmt(f)?;
iter.try_for_each(move |element| {
self.sep.fmt(f)?;
element.fmt(f)
})
}
}
}
}
This trait implementation compiles without complaint. Notice the bound on &'a C: IntoIterator
. Many containers implement IntoIterator
for a reference to themselves, to allow for iterating over references to the contained items (for instance, Vec
implements it here).
However, when I actually try to use my Join
struct, I get an unsatisfied trait bound:
fn main() {
let data = vec!["Hello", "World"];
let join = Join {
container: data,
sep: ", ",
};
println!("{}", join);
}
This code produces a compilation error:
error[E0277]: `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item` doesn't implement `std::fmt::Display`
--> src/main.rs:38:20
|
38 | println!("{}", join);
| ^^^^ `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item` cannot be formatted with the default formatter
|
= help: the trait `for<'a> std::fmt::Display` is not implemented for `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required because of the requirements on the impl of `std::fmt::Display` for `Join<std::vec::Vec<&str>, &str>`
= note: required by `std::fmt::Display::fmt`
The key line seems to be this:
the trait `for<'a> std::fmt::Display` is not implemented for `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item`
Unfortunately, the compiler doesn't actually tell me what the Item
type is, but based on my reading of the docs, it appears to be &T
, which in this case means &&str
.
Why doesn't the compiler think that &&str
implements Display
? I've tried this with many other types, like usize
and String
, and none of them work; they all fail with the same error. I know that these reference type don't directly implement Display
, but the implementation should be picked up automatically through deref coercion, right?
rust formatting traits
I'm building a library that implements string joins; that is, printing all the elements of a container separated by a separator. My basic design looks like this:
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Join<Container, Sep> {
container: Container,
sep: Sep,
}
impl<Container, Sep> fmt::Display for Join<Container, Sep>
where
for<'a> &'a Container: IntoIterator,
for<'a> <&'a Container as IntoIterator>::Item: fmt::Display,
Sep: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut iter = self.container.into_iter();
match iter.next() {
None => Ok(()),
Some(first) => {
first.fmt(f)?;
iter.try_for_each(move |element| {
self.sep.fmt(f)?;
element.fmt(f)
})
}
}
}
}
This trait implementation compiles without complaint. Notice the bound on &'a C: IntoIterator
. Many containers implement IntoIterator
for a reference to themselves, to allow for iterating over references to the contained items (for instance, Vec
implements it here).
However, when I actually try to use my Join
struct, I get an unsatisfied trait bound:
fn main() {
let data = vec!["Hello", "World"];
let join = Join {
container: data,
sep: ", ",
};
println!("{}", join);
}
This code produces a compilation error:
error[E0277]: `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item` doesn't implement `std::fmt::Display`
--> src/main.rs:38:20
|
38 | println!("{}", join);
| ^^^^ `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item` cannot be formatted with the default formatter
|
= help: the trait `for<'a> std::fmt::Display` is not implemented for `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required because of the requirements on the impl of `std::fmt::Display` for `Join<std::vec::Vec<&str>, &str>`
= note: required by `std::fmt::Display::fmt`
The key line seems to be this:
the trait `for<'a> std::fmt::Display` is not implemented for `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item`
Unfortunately, the compiler doesn't actually tell me what the Item
type is, but based on my reading of the docs, it appears to be &T
, which in this case means &&str
.
Why doesn't the compiler think that &&str
implements Display
? I've tried this with many other types, like usize
and String
, and none of them work; they all fail with the same error. I know that these reference type don't directly implement Display
, but the implementation should be picked up automatically through deref coercion, right?
rust formatting traits
rust formatting traits
edited Nov 18 '18 at 19:54
Lucretiel
asked Nov 18 '18 at 19:46
LucretielLucretiel
813925
813925
It looks like the compiler is treatingfor<'a> Display
as different fromDisplay
. This might be a bug in the compiler.
– Francis Gagné
Nov 18 '18 at 20:48
1
This indeed looks like a shortcoming in the compiler to me. You are right that the item type is&&str
, as you can easily verify by trying to compilelet _: <&Vec<&str> as IntoIterator>::Item = ();
. And&&str
definitely does implementDisplay
, as can be verified by adding<&&str as fmt::Display>::fmt(&&"hello", f)?;
to the body offmt()
. So this should definitely work. My guess is that<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item
isn't correctly normalized to&'a &str
for some reason.
– Sven Marnach
Nov 18 '18 at 20:52
1
See also github.com/rust-lang/rust/issues/24159 and the issues linked there. Often this can be worked around by adding an additional type parameter for the item type, and forcing it to be the actual item type by changing the trait boundIntoIterator
toIntoIterator<Item = T>
, whereT
is the new type parameter. This doesn't work int his case, though, due to the HRTBs.
– Sven Marnach
Nov 18 '18 at 21:19
You actually can do that in this case; I was able to try<T: Display> ... where &'a C: IntoIterator<Item=T>
. However, I got a different and somehow even less useful error message about unfulfilled lifetime bounds.
– Lucretiel
Nov 18 '18 at 22:48
1
@Lucretiel The reason this does not work is because of the lifetime ofItem
. In your example, the item type is&&str
, but annoted with the lifetime of the HRTB, it would have to be&'a &str
for all lifetimes'a
, which of course is impossible. That's what I meant in my previous comment.
– Sven Marnach
Nov 19 '18 at 7:38
|
show 1 more comment
It looks like the compiler is treatingfor<'a> Display
as different fromDisplay
. This might be a bug in the compiler.
– Francis Gagné
Nov 18 '18 at 20:48
1
This indeed looks like a shortcoming in the compiler to me. You are right that the item type is&&str
, as you can easily verify by trying to compilelet _: <&Vec<&str> as IntoIterator>::Item = ();
. And&&str
definitely does implementDisplay
, as can be verified by adding<&&str as fmt::Display>::fmt(&&"hello", f)?;
to the body offmt()
. So this should definitely work. My guess is that<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item
isn't correctly normalized to&'a &str
for some reason.
– Sven Marnach
Nov 18 '18 at 20:52
1
See also github.com/rust-lang/rust/issues/24159 and the issues linked there. Often this can be worked around by adding an additional type parameter for the item type, and forcing it to be the actual item type by changing the trait boundIntoIterator
toIntoIterator<Item = T>
, whereT
is the new type parameter. This doesn't work int his case, though, due to the HRTBs.
– Sven Marnach
Nov 18 '18 at 21:19
You actually can do that in this case; I was able to try<T: Display> ... where &'a C: IntoIterator<Item=T>
. However, I got a different and somehow even less useful error message about unfulfilled lifetime bounds.
– Lucretiel
Nov 18 '18 at 22:48
1
@Lucretiel The reason this does not work is because of the lifetime ofItem
. In your example, the item type is&&str
, but annoted with the lifetime of the HRTB, it would have to be&'a &str
for all lifetimes'a
, which of course is impossible. That's what I meant in my previous comment.
– Sven Marnach
Nov 19 '18 at 7:38
It looks like the compiler is treating
for<'a> Display
as different from Display
. This might be a bug in the compiler.– Francis Gagné
Nov 18 '18 at 20:48
It looks like the compiler is treating
for<'a> Display
as different from Display
. This might be a bug in the compiler.– Francis Gagné
Nov 18 '18 at 20:48
1
1
This indeed looks like a shortcoming in the compiler to me. You are right that the item type is
&&str
, as you can easily verify by trying to compile let _: <&Vec<&str> as IntoIterator>::Item = ();
. And &&str
definitely does implement Display
, as can be verified by adding <&&str as fmt::Display>::fmt(&&"hello", f)?;
to the body of fmt()
. So this should definitely work. My guess is that <&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item
isn't correctly normalized to &'a &str
for some reason.– Sven Marnach
Nov 18 '18 at 20:52
This indeed looks like a shortcoming in the compiler to me. You are right that the item type is
&&str
, as you can easily verify by trying to compile let _: <&Vec<&str> as IntoIterator>::Item = ();
. And &&str
definitely does implement Display
, as can be verified by adding <&&str as fmt::Display>::fmt(&&"hello", f)?;
to the body of fmt()
. So this should definitely work. My guess is that <&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item
isn't correctly normalized to &'a &str
for some reason.– Sven Marnach
Nov 18 '18 at 20:52
1
1
See also github.com/rust-lang/rust/issues/24159 and the issues linked there. Often this can be worked around by adding an additional type parameter for the item type, and forcing it to be the actual item type by changing the trait bound
IntoIterator
to IntoIterator<Item = T>
, where T
is the new type parameter. This doesn't work int his case, though, due to the HRTBs.– Sven Marnach
Nov 18 '18 at 21:19
See also github.com/rust-lang/rust/issues/24159 and the issues linked there. Often this can be worked around by adding an additional type parameter for the item type, and forcing it to be the actual item type by changing the trait bound
IntoIterator
to IntoIterator<Item = T>
, where T
is the new type parameter. This doesn't work int his case, though, due to the HRTBs.– Sven Marnach
Nov 18 '18 at 21:19
You actually can do that in this case; I was able to try
<T: Display> ... where &'a C: IntoIterator<Item=T>
. However, I got a different and somehow even less useful error message about unfulfilled lifetime bounds.– Lucretiel
Nov 18 '18 at 22:48
You actually can do that in this case; I was able to try
<T: Display> ... where &'a C: IntoIterator<Item=T>
. However, I got a different and somehow even less useful error message about unfulfilled lifetime bounds.– Lucretiel
Nov 18 '18 at 22:48
1
1
@Lucretiel The reason this does not work is because of the lifetime of
Item
. In your example, the item type is &&str
, but annoted with the lifetime of the HRTB, it would have to be &'a &str
for all lifetimes 'a
, which of course is impossible. That's what I meant in my previous comment.– Sven Marnach
Nov 19 '18 at 7:38
@Lucretiel The reason this does not work is because of the lifetime of
Item
. In your example, the item type is &&str
, but annoted with the lifetime of the HRTB, it would have to be &'a &str
for all lifetimes 'a
, which of course is impossible. That's what I meant in my previous comment.– Sven Marnach
Nov 19 '18 at 7:38
|
show 1 more comment
1 Answer
1
active
oldest
votes
Seems like a compiler limitation. You can work around it for now by writing the impl bound in terms of a private helper trait that represents "display with lifetime". This enables the compiler to see that for<'a> private::Display<'a>
implies fmt::Display
.
use std::fmt;
pub struct Join<Container, Sep> {
container: Container,
sep: Sep,
}
mod private {
use std::fmt;
pub trait Display<'a>: fmt::Display {}
impl<'a, T> Display<'a> for T where T: fmt::Display {}
}
impl<Container, Sep> fmt::Display for Join<Container, Sep>
where
for<'a> &'a Container: IntoIterator,
for<'a> <&'a Container as IntoIterator>::Item: private::Display<'a>,
Sep: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut iter = self.container.into_iter();
match iter.next() {
None => Ok(()),
Some(first) => {
first.fmt(f)?;
iter.try_for_each(move |element| {
self.sep.fmt(f)?;
element.fmt(f)
})
}
}
}
}
fn main() {
println!("{}", Join {
container: vec!["Hello", "World"],
sep: ", ",
});
}
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53364798%2fwhy-does-the-compiler-claim-that-a-generic-doesnt-implement-display-even-thou%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Seems like a compiler limitation. You can work around it for now by writing the impl bound in terms of a private helper trait that represents "display with lifetime". This enables the compiler to see that for<'a> private::Display<'a>
implies fmt::Display
.
use std::fmt;
pub struct Join<Container, Sep> {
container: Container,
sep: Sep,
}
mod private {
use std::fmt;
pub trait Display<'a>: fmt::Display {}
impl<'a, T> Display<'a> for T where T: fmt::Display {}
}
impl<Container, Sep> fmt::Display for Join<Container, Sep>
where
for<'a> &'a Container: IntoIterator,
for<'a> <&'a Container as IntoIterator>::Item: private::Display<'a>,
Sep: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut iter = self.container.into_iter();
match iter.next() {
None => Ok(()),
Some(first) => {
first.fmt(f)?;
iter.try_for_each(move |element| {
self.sep.fmt(f)?;
element.fmt(f)
})
}
}
}
}
fn main() {
println!("{}", Join {
container: vec!["Hello", "World"],
sep: ", ",
});
}
add a comment |
Seems like a compiler limitation. You can work around it for now by writing the impl bound in terms of a private helper trait that represents "display with lifetime". This enables the compiler to see that for<'a> private::Display<'a>
implies fmt::Display
.
use std::fmt;
pub struct Join<Container, Sep> {
container: Container,
sep: Sep,
}
mod private {
use std::fmt;
pub trait Display<'a>: fmt::Display {}
impl<'a, T> Display<'a> for T where T: fmt::Display {}
}
impl<Container, Sep> fmt::Display for Join<Container, Sep>
where
for<'a> &'a Container: IntoIterator,
for<'a> <&'a Container as IntoIterator>::Item: private::Display<'a>,
Sep: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut iter = self.container.into_iter();
match iter.next() {
None => Ok(()),
Some(first) => {
first.fmt(f)?;
iter.try_for_each(move |element| {
self.sep.fmt(f)?;
element.fmt(f)
})
}
}
}
}
fn main() {
println!("{}", Join {
container: vec!["Hello", "World"],
sep: ", ",
});
}
add a comment |
Seems like a compiler limitation. You can work around it for now by writing the impl bound in terms of a private helper trait that represents "display with lifetime". This enables the compiler to see that for<'a> private::Display<'a>
implies fmt::Display
.
use std::fmt;
pub struct Join<Container, Sep> {
container: Container,
sep: Sep,
}
mod private {
use std::fmt;
pub trait Display<'a>: fmt::Display {}
impl<'a, T> Display<'a> for T where T: fmt::Display {}
}
impl<Container, Sep> fmt::Display for Join<Container, Sep>
where
for<'a> &'a Container: IntoIterator,
for<'a> <&'a Container as IntoIterator>::Item: private::Display<'a>,
Sep: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut iter = self.container.into_iter();
match iter.next() {
None => Ok(()),
Some(first) => {
first.fmt(f)?;
iter.try_for_each(move |element| {
self.sep.fmt(f)?;
element.fmt(f)
})
}
}
}
}
fn main() {
println!("{}", Join {
container: vec!["Hello", "World"],
sep: ", ",
});
}
Seems like a compiler limitation. You can work around it for now by writing the impl bound in terms of a private helper trait that represents "display with lifetime". This enables the compiler to see that for<'a> private::Display<'a>
implies fmt::Display
.
use std::fmt;
pub struct Join<Container, Sep> {
container: Container,
sep: Sep,
}
mod private {
use std::fmt;
pub trait Display<'a>: fmt::Display {}
impl<'a, T> Display<'a> for T where T: fmt::Display {}
}
impl<Container, Sep> fmt::Display for Join<Container, Sep>
where
for<'a> &'a Container: IntoIterator,
for<'a> <&'a Container as IntoIterator>::Item: private::Display<'a>,
Sep: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut iter = self.container.into_iter();
match iter.next() {
None => Ok(()),
Some(first) => {
first.fmt(f)?;
iter.try_for_each(move |element| {
self.sep.fmt(f)?;
element.fmt(f)
})
}
}
}
}
fn main() {
println!("{}", Join {
container: vec!["Hello", "World"],
sep: ", ",
});
}
answered Nov 18 '18 at 21:16
dtolnaydtolnay
3,50711633
3,50711633
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53364798%2fwhy-does-the-compiler-claim-that-a-generic-doesnt-implement-display-even-thou%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
It looks like the compiler is treating
for<'a> Display
as different fromDisplay
. This might be a bug in the compiler.– Francis Gagné
Nov 18 '18 at 20:48
1
This indeed looks like a shortcoming in the compiler to me. You are right that the item type is
&&str
, as you can easily verify by trying to compilelet _: <&Vec<&str> as IntoIterator>::Item = ();
. And&&str
definitely does implementDisplay
, as can be verified by adding<&&str as fmt::Display>::fmt(&&"hello", f)?;
to the body offmt()
. So this should definitely work. My guess is that<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item
isn't correctly normalized to&'a &str
for some reason.– Sven Marnach
Nov 18 '18 at 20:52
1
See also github.com/rust-lang/rust/issues/24159 and the issues linked there. Often this can be worked around by adding an additional type parameter for the item type, and forcing it to be the actual item type by changing the trait bound
IntoIterator
toIntoIterator<Item = T>
, whereT
is the new type parameter. This doesn't work int his case, though, due to the HRTBs.– Sven Marnach
Nov 18 '18 at 21:19
You actually can do that in this case; I was able to try
<T: Display> ... where &'a C: IntoIterator<Item=T>
. However, I got a different and somehow even less useful error message about unfulfilled lifetime bounds.– Lucretiel
Nov 18 '18 at 22:48
1
@Lucretiel The reason this does not work is because of the lifetime of
Item
. In your example, the item type is&&str
, but annoted with the lifetime of the HRTB, it would have to be&'a &str
for all lifetimes'a
, which of course is impossible. That's what I meant in my previous comment.– Sven Marnach
Nov 19 '18 at 7:38