Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a blanket impl for core::fmt::Write if there is an impl for std::io::Write #77733

Open
camelid opened this issue Oct 9, 2020 · 10 comments
Open
Labels
A-fmt Area: std::fmt C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@camelid
Copy link
Member

camelid commented Oct 9, 2020

Advantages

Types which implement std::io::Write get an implementation of core::fmt::Write for free. core::fmt::Write is useful if you only want to accept UTF-8 data.

Disadvantages

Could it hurt type inference? If so, is there some way of allowing impl core::fmt::Write for Foo {} (i.e., empty impl body) if there is an impl for std::io::Write?


Cc pulldown-cmark/pulldown-cmark#492

@camelid camelid added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. A-fmt Area: std::fmt labels Oct 9, 2020
@Lonami
Copy link
Contributor

Lonami commented Oct 9, 2020

This has the potential of causing many panic-by-default implementations, which would happen for virtually any io::Write that writes non-UTF8 data. I would say the opposite would make more sense (write fmt, get io for free), but even then I feel like the two are too different (there's a separation for a reason, one to be used for binary data and the other for UTF-8 strings).

@Lonami
Copy link
Contributor

Lonami commented Oct 9, 2020

What about a #[derive(fmt::Write)] if the type has an impl io::Write? This way it's clear that the binary serialization of a type is in fact just a UTF-8 string, and users opt-in to this knowing well the io::Write should be UTF-8 compatible.

Note this could also go the other way (impl fmt::Write and #[derive(io::Write)]) which I think makes more sense.

@workingjubilee
Copy link
Contributor

workingjubilee commented Oct 9, 2020

As noted by Lonami, they are not quite describing the same function, and while the #[derive] idea is interesting, observationally derives tend to be pretty conservative, as they rarely make new assertions but rather only are valid if certain requirements are met.

It would be desirable to have some kind of meta-abstraction that touches both, but I don't think that is practical in current Rust, I think.

@Lonami
Copy link
Contributor

Lonami commented Oct 9, 2020

If we go with the impl fmt::Write and #[derive(io::Write)] route, no new assertions are made (fmt already ensures the data is valid UTF-8, and io would just write the UTF-8-encoded bytes). Not sure how niche or useful this would turn out to be though, might just be easier to keep the StrWrite (why not automatically impl that on things that implement fmt::Write and have it provide io::Write?).

@jonas-schievink
Copy link
Contributor

Adding a blanket impl is a breaking change because the impl can conflict with downstream impls.

@camelid
Copy link
Member Author

camelid commented Oct 9, 2020

This has the potential of causing many panic-by-default implementations, which would happen for virtually any io::Write that writes non-UTF8 data. I would say the opposite would make more sense (write fmt, get io for free), but even then I feel like the two are too different (there's a separation for a reason, one to be used for binary data and the other for UTF-8 strings).

Hmm, I don't think the opposite would be possible; the opposite would involve more panic-by-default impls since fmt's API is a subset of io's.

Adding a blanket impl is a breaking change because the impl can conflict with downstream impls.

Hmm, that's unfortunate. Maybe a #[derive] would make sense as Lonami mentioned?

@camelid
Copy link
Member Author

camelid commented Oct 9, 2020

At least it would be nice to have fmt::Write work on, e.g., files.

@ollie27
Copy link
Member

ollie27 commented Oct 9, 2020

An adapter struct might be the way to solve this. There is even a struct already in std that could maybe be made public:

rust/library/std/src/io/mod.rs

Lines 1496 to 1501 in 5ddef54

// Create a shim which translates a Write to a fmt::Write and saves
// off I/O errors. instead of discarding them
struct Adaptor<'a, T: ?Sized + 'a> {
inner: &'a mut T,
error: Result<()>,
}

@lambda-fairy
Copy link
Contributor

I don't think a blanket impl would make sense anyway, because the two traits are at different levels of abstraction (bytes vs text). The standard library tends to make encoding explicit elsewhere (e.g. str::from_utf8) and we should be consistent with that.

@camelid camelid added the C-feature-request Category: A feature request, i.e: not implemented / a PR. label Jan 31, 2021
@SUPERCILEX
Copy link
Contributor

rust-lang/libs-team#133 is trying to solve this issue, but in the meantime I have a crate which should do the job: https://github.com/SUPERCILEX/io-adapters

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-fmt Area: std::fmt C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants