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

ACP: Add finish_non_exhaustive to DebugList, DebugMap, andDebugTuple #248

Closed
tgross35 opened this issue Jul 26, 2023 · 7 comments
Closed
Labels
ACP-accepted API Change Proposal is accepted (seconded with no objections) api-change-proposal A proposal to add or alter unstable APIs in the standard libraries T-libs-api

Comments

@tgross35
Copy link

Proposal

Problem statement

Many formatting implementations may want to print only a subset of items from a list, set, or map. Subsets can be printed directly (e.g., .item(my_slice[..10])) but there is no clear way to indicate that there are more elements not dislayed.

Motivating examples or use cases

Currently, one of the best solutions to showing non exhaustiveness looks as follows, in this simple wrapper type that prints up to 6 elements in a buffer:

struct TruncatedPrinter<'a, T>(&'a [T]);

impl<'a, T: fmt::Debug> fmt::Display for TruncatedPrinter<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // print at most 6 elements (don't ever omit the `..`, for simplicity)
        let to_print = min(self.0.len(), 6);
        f.debug_list().entries(self.0[..to_print]).entry(&{..}).finish()
    }
}

The output looks like the following:

[1, 2, 3, 4, 5, 6, ..]

This is correct, but (1) is less clear and less discoverable than finish_non_exhaustive, and (2) relies on the debug implementation of RangeFull (though this is unlikely to change).

Solution sketch

This proposes adding a finish_non_exhaustive method to other DebugX types, to mirror what is already possible with DebugStruct. New API would be:

impl<'a, 'b: 'a> DebugList<'a, 'b> {
    pub fn finish_non_exhaustive(&mut self) -> Result<(), Error>;
}

impl<'a, 'b: 'a> DebugMap<'a, 'b> {
    pub fn finish_non_exhaustive(&mut self) -> Result<(), Error>;
}

impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
    pub fn finish_non_exhaustive(&mut self) -> Result<(), Error>;
}

Each of these results should add an element .. to the output list, similar to DebugStruct's behavior.

Alternatives

An alternative could be to provide a NonExhaustive element that implements Debug and prints .., which could be passed to entry. This would have the advantage of giving an easy way to omit items in the middle of a list, e.g. [1, 2, ..., 6], and would be more clear than the status quo &{..}.

This option was not included in order to match DebugStruct's precedent.

Links and related work

Existing API: DebugStruct::finish_non_exhaustive

debug_non_exhaustive discussion and implmentation: rust-lang/rust#67364

That issue does discuss adding the implementation for other DebugX types, but I do not see any specific reason that they were excluded outside of keeping the MVP minimal. There are a few comments expressing desire for DebugTuple to provide this method.

What happens now?

This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.

Possible responses

The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):

  • We think this problem seems worth solving, and the standard library might be the right place to solve it.
  • We think that this probably doesn't belong in the standard library.

Second, if there's a concrete solution:

  • We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
  • We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
@tgross35 tgross35 added api-change-proposal A proposal to add or alter unstable APIs in the standard libraries T-libs-api labels Jul 26, 2023
@tgross35
Copy link
Author

I wasn't sure whether this would need an ACP since rust-lang/rust#67364 didn't have any specific proposal, but figured it doesn't hurt before doing the work.

@pitaj
Copy link

pitaj commented Jul 26, 2023

I think we should heavily discourage abbreviating the debug output of map or list. I'm not sure if making that easier is the right course of action.

@tgross35
Copy link
Author

Why should it be discouraged? When I have types that contain large slices, I much prefer their standard debug output only print 20 relevant bytes rather than a whole 100kB+ buffer. It makes it easier to see the other fields while still giving a hint about the content.

Same thing for messages related to maps or sets, where you want to indicate a few special or problamatic items but everything else is OK.

@pitaj
Copy link

pitaj commented Jul 26, 2023

Personally, I want my debug output to include all the information available. Hiding information just because there's a lot of it seems antithetical to the whole idea of debug.

Like if I'm trying to see what's in some buffer, I don't want it to just show me the first few bytes.

@tgross35
Copy link
Author

I would counter that if I'm trying to see what's in some buffer, I would print my_struct.buffer specifically. But if I'm trying to see any of the other fields in my_struct, I would rather see a truncated buffer than either (1) no field at all, or (2) fill up my terminal scrollback printing one field (especially in pretty printing mode, one line per byte is rough). I'm not suggesting truncated printing become the default, just that it would be nice if there's an easy way to indicate that there's more data not shown.

But I've been working with large blobs recently, so I admit the need isn't in the most common workflow.

@CAD97
Copy link

CAD97 commented Jul 30, 2023

The existence of this API doesn't particularly encourage eliding details from the Debug representation. Custom implementations of Debug can and already do omit some information for clarity. Debug generally isn't intended to display/debug the internal structure, but the outer data "shape," and in that context, too much information can easily obscure the more useful higher level structure. What's really wanted is a foldable inspector like typically provided by debugger views, including toggle between high-level and low-level details of the inspected type.

For clarity, there's no suggestion to use this for any std types at the moment, and these are just provided for downstream impl Debug. #[non_exhaustive] is permitted on tuples, so at a minimum finish_non_exhaustive should be available on DebugTuple.

If it's considered important, the method documentation can carry an admonition not to use it to arbitrarily hide data that would likely be useful to the containing context, and to consider checking alt mode ({:#?}) and not eliding data in that case.


For current stable, instead of &{..} (ab)using RangeFull, using &format_args!("..") is another option, and a useful trick to get an adhoc Display + Debug object.

Future work could be allowing precision specifiers et al to control how the Debug* format helpers present data.

@Amanieu
Copy link
Member

Amanieu commented Jul 9, 2024

We discussed this in the libs-api meeting and we are happy to add these. A similar method should also be added to DebugSet.

@Amanieu Amanieu closed this as completed Jul 9, 2024
@Amanieu Amanieu added the ACP-accepted API Change Proposal is accepted (seconded with no objections) label Jul 9, 2024
tgross35 added a commit to tgross35/rust that referenced this issue Jul 19, 2024
Add a `.finish_non_exhaustive()` method to `DebugTuple`, `DebugSet`,
`DebugList`, and `DebugMap`. This indicates that the structures have
remaining items with `..`.

This implements the ACP at
<rust-lang/libs-team#248>.
tgross35 added a commit to tgross35/rust that referenced this issue Jul 21, 2024
Add a `.finish_non_exhaustive()` method to `DebugTuple`, `DebugSet`,
`DebugList`, and `DebugMap`. This indicates that the structures have
remaining items with `..`.

This implements the ACP at
<rust-lang/libs-team#248>.
tgross35 added a commit to tgross35/rust that referenced this issue Jul 21, 2024
Add a `.finish_non_exhaustive()` method to `DebugTuple`, `DebugSet`,
`DebugList`, and `DebugMap`. This indicates that the structures have
remaining items with `..`.

This implements the ACP at
<rust-lang/libs-team#248>.
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Aug 21, 2024
…, r=Noratrieb

Implement `debug_more_non_exhaustive`

This implements the ACP at rust-lang/libs-team#248, adding `.finish_non_exhaustive()` for `DebugTuple`, `DebugSet`, `DebugList`, and `DebugMap`.

Also used this as an opportunity to make some documentation and tests more readable by using raw strings instead of escaped quotes.

Tracking issue: rust-lang#127942
jieyouxu added a commit to jieyouxu/rust that referenced this issue Aug 22, 2024
…, r=Noratrieb

Implement `debug_more_non_exhaustive`

This implements the ACP at rust-lang/libs-team#248, adding `.finish_non_exhaustive()` for `DebugTuple`, `DebugSet`, `DebugList`, and `DebugMap`.

Also used this as an opportunity to make some documentation and tests more readable by using raw strings instead of escaped quotes.

Tracking issue: rust-lang#127942
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Aug 22, 2024
Rollup merge of rust-lang#127945 - tgross35:debug-more-non-exhaustive, r=Noratrieb

Implement `debug_more_non_exhaustive`

This implements the ACP at rust-lang/libs-team#248, adding `.finish_non_exhaustive()` for `DebugTuple`, `DebugSet`, `DebugList`, and `DebugMap`.

Also used this as an opportunity to make some documentation and tests more readable by using raw strings instead of escaped quotes.

Tracking issue: rust-lang#127942
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ACP-accepted API Change Proposal is accepted (seconded with no objections) api-change-proposal A proposal to add or alter unstable APIs in the standard libraries T-libs-api
Projects
None yet
Development

No branches or pull requests

4 participants