Allow only implementing Read::read_buf rust-lang/rust#106643

Open

21 comments and reviews loaded in 1.02s

WaffleLapkin Avatar
WaffleLapkin on 2023-01-09 16:37:46 UTC · edited
WaffleLapkin Avatar
WaffleLapkin on 2023-01-09 16:37:46 UTC · edited
View on GitHub

View all comments

This PR allows users to only implement Read::read_buf, without the need for implementing Read::read. rustc_must_implement_one_of annotation ensures that at least one of the methods is implemented, so that the default impls don't create infinite recursion.

Note that Read::read_buf is unstable, so this doesn't change anything on stable, there you still need to implement Read::read, since you can't implement Read::read_buf. Thus, we don't expose rustc_must_implement_one_of to stable.

r? @thomcc

❤️3
rustbot Avatar
rustbot on 2023-01-09 16:37:54 UTC
rustbot Avatar
rustbot on 2023-01-09 16:37:54 UTC
View on GitHub

Hey! It looks like you've submitted a new PR for the library teams!

If this PR contains changes to any rust-lang/rust public library APIs then please comment with @rustbot label +T-libs-api -T-libs to tag it appropriately. If this PR contains changes to any unstable APIs please edit the PR description to add a link to the relevant API Change Proposal or create one if you haven't already. If you're unsure where your change falls no worries, just leave it as is and the reviewer will take a look and make a decision to forward on if necessary.

Examples of T-libs-api changes:

  • Stabilizing library features
  • Introducing insta-stable changes such as new implementations of existing stable traits on existing stable types
  • Introducing new or changing existing unstable library APIs (excluding permanently unstable features / features without a tracking issue)
  • Changing public documentation in ways that create new stability guarantees
  • Changing observable runtime behavior of library APIs
thomcc Avatar
thomcc on 2023-01-12 01:06:03 UTC
thomcc Avatar
thomcc on 2023-01-12 01:06:03 UTC
View on GitHub

For a summary:

This extends std::io::Read by allowing users to implement Read by only providing a Read::read_buf impl, and not a Read::read impl. It does this using the unstable attribute rustc_must_implement_one_of1.

#[rustc_must_implement_one_of(read, read_buf)]
pub trait std::io::Read {
    // (new)
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        let mut buf = BorrowedBuf::from(buf);
        self.read_buf(buf.unfilled()).map(|()| buf.len())
    }
    // (existing)
    fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
        default_read_buf(|b| self.read(b), buf)
    }
}

This does not make rustc_must_implement_must_of user-visible, since read_buf is unstable (altho IMO it could easily be forgotten when stabilizing).

Whether or not we want to start using the #[rustc_must_implement_one_of(...)] attribute on the stdlib is a question that's split between lang (if they think the language feature has a path to stabilization) and libs-api (if they are okay with using it in that case). I'm on neither of these teams, and don't feel that strongly (but weakly, I think it probably needs a t-libs-api meeting to discuss after asking t-lang if it's okay start experimenting with)

r? @joshtriplett (who is on both relevant teams)

Footnotes

  1. Which allows a trait to have a pair (or perhaps more?) of functions each of which are implemented in terms of the other, so long as the implementation user provides an impl of at least one of them for their type. I cannot seem to find a tracking method for it, but do recall discussions involving it in the past.

❤️2
joshtriplett Avatar
joshtriplett on 2023-01-12 03:02:35 UTC
joshtriplett Avatar
joshtriplett on 2023-01-12 03:02:35 UTC
View on GitHub

I'm nominating this for T-lang to consider that question: are we OK with people using rustc_must_implement_one_of in stable standard-library APIs?

joshtriplett Avatar
joshtriplett on 2023-01-12 03:04:26 UTC
joshtriplett Avatar
joshtriplett on 2023-01-12 03:04:26 UTC
View on GitHub

Separately, I'm nominating this for libs-api. Are we OK with (on nightly) allowing people to implement read_buf and not read?

I am in favor, and in the absence of objections I'd r+ this once we have T-lang confirmation.

🚀1
joshtriplett Avatar
joshtriplett on 2023-01-17 16:18:46 UTC
joshtriplett Avatar
joshtriplett on 2023-01-17 16:18:46 UTC
View on GitHub

Discussed this asynchronously with some T-lang folks, and one item that came up: we'd really like to see rustc_must_implement_one_of at least documented in the Rust reference. And once it's documented, we may well be up for just stabilizing it as must_implement_one_of.

WaffleLapkin Avatar
WaffleLapkin on 2023-01-30 06:59:45 UTC
WaffleLapkin Avatar
WaffleLapkin on 2023-01-30 06:59:45 UTC
View on GitHub

I've created a tracking issue: #107460

joshtriplett Avatar
joshtriplett on 2023-01-31 16:12:50 UTC
joshtriplett Avatar
joshtriplett on 2023-01-31 16:12:50 UTC
View on GitHub

Removing I-libs-api-nominated for now, until the attribute has documentation in the reference.

joshtriplett Avatar
joshtriplett on 2023-01-31 19:30:38 UTC
joshtriplett Avatar
joshtriplett on 2023-01-31 19:30:38 UTC
View on GitHub

We discussed this once more in @rust-lang/lang, and confirmed that we have no objection to adding this on nightly.

@bors r+

We'd consider it a blocker for stabilization of read_buf that the attribute be either stabilized or at least documented in the reference or similar.

❤️1
bors Avatar
bors on 2023-01-31 19:30:40 UTC
bors Avatar
bors on 2023-01-31 19:30:40 UTC
View on GitHub

📌 Commit d7dac91 has been approved by joshtriplett

It is now in the queue for this repository.

dtolnay Avatar
dtolnay on 2023-01-31 20:00:16 UTC
dtolnay Avatar
dtolnay on 2023-01-31 20:00:16 UTC
View on GitHub

I am concerned about this negatively affecting the experience of non-nightly users. For example with this code:

struct MyRead;

impl std::io::Read for MyRead {}

current stable gives you this error:

error[E0046]: not all trait items implemented, missing: `read`
 --> src/main.rs:3:1
  |
3 | impl std::io::Read for MyRead {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `read` in implementation
  |
  = help: implement the missing item: `fn read(&mut self, _: &mut [u8]) -> Result<usize, std::io::Error> { todo!() }`

whereas rustc built from this PR gives this error:

error[E0046]: not all trait items implemented, missing one of: `read`, `read_buf`
   --> src/main.rs:3:1
    |
3   | impl std::io::Read for MyRead {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing one of `read`, `read_buf` in implementation
    |
note: required because of this annotation
   --> /git/rust/library/std/src/io/mod.rs:552:1
    |
552 | #[rustc_must_implement_one_of(read, read_buf)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Some concerns:

  • I only know how to build rustc in dev mode. When it's built in stable mode, is it still going to tell the user to consider implementing the unstable read_buf method, which they cannot do on a stable toolchain? That would be undesirable and it would be good to have a UI test covering this case.

  • Notice that even aside from "missing read" vs "missing one of read or read_buf", the new error is less helpful because it does not give you the signature you need to put, so there isn't a signature you can copy straight from the error into your code. Would it make sense to give 2 "help:", one with each signature?

  • How does this impact rust-analyzer? When you write a trait impl and tell it to autofill the required methods, is it going to put both? neither? just the alphanumerically first one or the one on the lower line number in libstd? My sense is a lot of people rely on this autofill when writing trait impls in an IDE.

@bors r-

joshtriplett Avatar
joshtriplett on 2023-01-31 20:27:46 UTC
joshtriplett Avatar
joshtriplett on 2023-01-31 20:27:46 UTC
View on GitHub

When it's built in stable mode, is it still going to tell the user to consider implementing the unstable read_buf method, which they cannot do on a stable toolchain? That would be undesirable and it would be good to have a UI test covering this case.

Valid; I agree that we should avoid suggesting implementing a method that you can't implement.

@WaffleLapkin, would that be feasible, and can you add a test verifying that behavior as well?

Notice that even aside from "missing read" vs "missing one of read or read_buf", the new error is less helpful because it does not give you the signature you need to put, so there isn't a signature you can copy straight from the error into your code. Would it make sense to give 2 "help:", one with each signature?

Fair. That seems like a reasonable solution for the common case.

WaffleLapkin Avatar
WaffleLapkin on 2023-02-01 12:46:46 UTC
WaffleLapkin Avatar
WaffleLapkin on 2023-02-01 12:46:46 UTC
View on GitHub

How does this impact rust-analyzer? When you write a trait impl and tell it to autofill the required methods, is it going to put both? neither? just the alphanumerically first one or the one on the lower line number in libstd? My sense is a lot of people rely on this autofill when writing trait impls in an IDE.

@dtolnay thanks for rising these concerns! My guess is that r-a will not autofill any methods, as they all have default bodies. I'll look into how we can support this feature in r-a.

WaffleLapkin, would that be feasible, and can you add a test verifying that behavior as well?

@joshtriplett This should be feasible. I'll make a PR that removes mentions of unstable methods from this diagnostic and shows the signatures soon™.

For now, marking this PR as blocked.

Dylan-DPC Avatar
Dylan-DPC on 2024-09-23 13:18:36 UTC · edited
Dylan-DPC Avatar
Dylan-DPC on 2024-09-23 13:18:36 UTC · edited
View on GitHub

This should be feasible. I'll make a PR that removes mentions of unstable methods from this diagnostic and shows the signatures soon

@WaffleLapkin has there been any progress on this? thanks

WaffleLapkin Avatar
WaffleLapkin on 2024-09-24 12:49:58 UTC
WaffleLapkin Avatar
WaffleLapkin on 2024-09-24 12:49:58 UTC
View on GitHub

@Dylan-DPC I made 0 progress on this so far. In my opinion r-a support is a bigger concern here (although I did not progress there either). I don't have much time to work on this recently :(

Also note that there were some concerns raised about read and read_buf having incompatible semantics, although I don't recall where exactly (maybe some tracking issue?), so I'm not sure if this can be supported at all.

👍1
rustbot Avatar
rustbot on 2025-10-26 22:09:59 UTC · hidden as outdated
rustbot Avatar
rustbot on 2025-10-26 22:09:59 UTC · hidden as outdated
View on GitHub

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

rustbot Avatar
rustbot on 2025-12-02 10:14:32 UTC · hidden as outdated
rustbot Avatar
rustbot on 2025-12-02 10:14:32 UTC · hidden as outdated
View on GitHub

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

rust-log-analyzer Avatar
rust-log-analyzer on 2025-12-02 10:58:55 UTC · hidden as outdated
rust-log-analyzer Avatar
rust-log-analyzer on 2025-12-02 10:58:55 UTC · hidden as outdated
View on GitHub

The job aarch64-gnu-llvm-20-1 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)

---- [rustdoc] tests/rustdoc/jump-to-def/non-local-method.rs stdout ----
------python3 stdout------------------------------

------python3 stderr------------------------------
19: has check failed
 `XPATH PATTERN` did not match
     //@ has - '//a[@href="{{channel}}/std/io/trait.Read.html#tymethod.read"]' 'read'

Encountered 1 errors

------------------------------------------

error: htmldocck failed!
status: exit status: 1
command: "/usr/bin/python3" "/checkout/src/etc/htmldocck.py" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/rustdoc/jump-to-def/non-local-method" "/checkout/tests/rustdoc/jump-to-def/non-local-method.rs"
stdout: none
--- stderr -------------------------------
19: has check failed
 `XPATH PATTERN` did not match
     //@ has - '//a[@href="{{channel}}/std/io/trait.Read.html#tymethod.read"]' 'read'

Encountered 1 errors
------------------------------------------

---- [rustdoc] tests/rustdoc/jump-to-def/non-local-method.rs stdout end ----
rustbot Avatar
rustbot on 2025-12-13 13:03:43 UTC · hidden as outdated
rustbot Avatar
rustbot on 2025-12-13 13:03:43 UTC · hidden as outdated
View on GitHub

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

rust-log-analyzer Avatar
rust-log-analyzer on 2025-12-13 13:45:20 UTC · hidden as outdated
rust-log-analyzer Avatar
rust-log-analyzer on 2025-12-13 13:45:20 UTC · hidden as outdated
View on GitHub

The job aarch64-gnu-llvm-20-1 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)

---- [rustdoc] tests/rustdoc/jump-to-def/non-local-method.rs stdout ----
------python3 stdout------------------------------

------python3 stderr------------------------------
19: has check failed
 `XPATH PATTERN` did not match
     //@ has - '//a[@href="{{channel}}/std/io/trait.Read.html#tymethod.read"]' 'read'

Encountered 1 errors

------------------------------------------

error: htmldocck failed!
status: exit status: 1
command: "/usr/bin/python3" "/checkout/src/etc/htmldocck.py" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/rustdoc/jump-to-def/non-local-method" "/checkout/tests/rustdoc/jump-to-def/non-local-method.rs"
stdout: none
--- stderr -------------------------------
19: has check failed
 `XPATH PATTERN` did not match
     //@ has - '//a[@href="{{channel}}/std/io/trait.Read.html#tymethod.read"]' 'read'

Encountered 1 errors
------------------------------------------

---- [rustdoc] tests/rustdoc/jump-to-def/non-local-method.rs stdout end ----
rustbot Avatar
rustbot on 2026-04-09 13:53:07 UTC
rustbot Avatar
rustbot on 2026-04-09 13:53:07 UTC
View on GitHub

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

rust-log-analyzer Avatar
rust-log-analyzer on 2026-04-09 14:34:20 UTC
rust-log-analyzer Avatar
rust-log-analyzer on 2026-04-09 14:34:20 UTC
View on GitHub

The job aarch64-gnu-llvm-21-1 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
##[endgroup]
Executing "/scripts/stage_2_test_set1.sh"
+ /scripts/stage_2_test_set1.sh
+ '[' 1 == 1 ']'
+ echo 'PR_CI_JOB set; skipping tidy'
+ SKIP_TIDY='--skip tidy'
+ ../x.py --stage 2 test --skip tidy --skip compiler --skip src
PR_CI_JOB set; skipping tidy
##[group]Building bootstrap
    Finished `dev` profile [unoptimized] target(s) in 0.04s
##[endgroup]
---

---- [rustdoc-html] tests/rustdoc-html/jump-to-def/non-local-method.rs stdout ----
------python3 stdout------------------------------

------python3 stderr------------------------------
19: has check failed
 `XPATH PATTERN` did not match
     //@ has - '//a[@href="{{channel}}/std/io/trait.Read.html#tymethod.read"]' 'read'

Encountered 1 errors

------------------------------------------

error: htmldocck failed!
status: exit status: 1
command: "/usr/bin/python3" "/checkout/src/etc/htmldocck.py" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/rustdoc-html/jump-to-def/non-local-method" "/checkout/tests/rustdoc-html/jump-to-def/non-local-method.rs"
stdout: none
--- stderr -------------------------------
19: has check failed
 `XPATH PATTERN` did not match
     //@ has - '//a[@href="{{channel}}/std/io/trait.Read.html#tymethod.read"]' 'read'

Encountered 1 errors
------------------------------------------

---- [rustdoc-html] tests/rustdoc-html/jump-to-def/non-local-method.rs stdout end ----