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

Tracking Issue for bound_as_ref #80996

Open
1 of 3 tasks
glittershark opened this issue Jan 14, 2021 · 22 comments
Open
1 of 3 tasks

Tracking Issue for bound_as_ref #80996

glittershark opened this issue Jan 14, 2021 · 22 comments
Labels
C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@glittershark
Copy link
Contributor

glittershark commented Jan 14, 2021

Feature gate: #![feature(bound_as_ref)]

This is a tracking issue for providing as_ref and as_mut on bound. These methods mirror similar methods on the Option type.

Public API

impl<T> Bound<T> {
  pub fn as_ref(&self) -> Bound<&T>;
  pub fn as_mut(&mut self) -> Bound<&mut T>;
}

Steps / History

Unresolved Questions

  • None yet.
@glittershark glittershark added C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Jan 14, 2021
@tesuji

This comment has been minimized.

@drmason13

This comment has been minimized.

@glittershark

This comment has been minimized.

@glittershark

This comment has been minimized.

@glittershark
Copy link
Contributor Author

This has been unstable since Jan 14 and seems relatively uncontroversial, modulo that issue, so I'd love to get the stabilization process kicked off. @dtolnay you reviewed the original PR, any qualms with that?

@dtolnay
Copy link
Member

dtolnay commented Jun 7, 2021

as_ref seems well motivated to me and I would be prepared to propose FCP. Given that RangeBounds revolves around returning Bound<&T>, having an easy way to acquire them from non-borrowed data is compelling.

But does anybody so far have a real world use case for as_mut? Can we delete it?

@glittershark
Copy link
Contributor Author

But does anybody so far have a real world use case for as_mut? Can we delete it?

it would seem weird to have as_ref and not as_mut, wouldn't it? especially since Option has both. I feel like that violates principle of least surprise.

@dtolnay
Copy link
Member

dtolnay commented Jun 8, 2021

Okay but Option::as_mut is widely used and useful.

@glittershark
Copy link
Contributor Author

I guess my point is that if Option::as_mut is useful, then Bound::as_mut is reasonably likely to be useful as well, and I don't see a downside to including it - but that latter point is something that maintainers are much more likely to have opinions about than me 😄. Definitely not my hill to die on, though.

@dtolnay
Copy link
Member

dtolnay commented Jun 29, 2021

@rfcbot poll libs-api Delete core::ops::Bound::as_mut because it is useless as far as any of us can tell
@rfcbot poll libs-api Keep core::ops::Bound::as_mut because wherever a as_ref exists, so must as_mut, even if useless

@rfcbot
Copy link

rfcbot commented Jun 29, 2021

Team member @dtolnay has asked teams: T-libs-api, for consensus on:

Delete core::ops::Bound::as_mut because it is useless as far as any of us can tell

@rfcbot
Copy link

rfcbot commented Jun 29, 2021

Team member @dtolnay has asked teams: T-libs-api, for consensus on:

Keep core::ops::Bound::as_mut because wherever a as_ref exists, so must as_mut, even if useless

@rust-lang rust-lang deleted a comment from rfcbot Jun 29, 2021
@glittershark
Copy link
Contributor Author

That poll wording seems a little... leading, doesn't it? I think callingas_mut decisively useless isn't quite right - what if you want to call an associated function on the endpoint of a bound that takes &mut self?

@BurntSushi
Copy link
Member

@glittershark Could you perhaps elaborate a bit more? Is it possible to show a short code sample?

I think that if as_mut is quite literally useless---in that there really no possible use for it---then I'd be against its inclusion. Principally because I would see its presence as confusing. e.g., "Why is std giving me a routine that has no possible use?"

But if it affords a little extra flexibility or convenience in some cases, then I think i'd be inclined to add it, mostly because my bar for adding as_mut is so low given it is a strong convention.

@Waridley
Copy link

Waridley commented Sep 9, 2021

what if you want to call an associated function on the endpoint of a bound that takes &mut self?

Then you'd still have to match on the Bound in order to get to the inner &mut T to call that function, right? map_mut seems more applicable in that case. Matching on &mut Bound<T> should still give you &mut T in the match arms.

@glittershark
Copy link
Contributor Author

Then you'd still have to match on the Bound in order to get to the inner &mut T to call that function, right? map_mut seems more applicable in that case. Matching on &mut Bound<T> should still give you &mut T in the match arms.

yeah, that's fair - it's not often you'd want .as_mut() without immediately being followed by a .map(...)... but isn't that true for Option as well? Basically any time I think about this I net out at preferring API consistency over all else.

@glittershark
Copy link
Contributor Author

I did have a bit of a lightbulb about this since last time I was engaged in the discussion, which was that perhaps (tell me if I'm wrong) @dtolnay's hesitation about .as_mut() come from an assumption that all Bounds originate at, or are constructed to be returned by, a call to RangeBounds::start_bound or ::end_bound - both of which return Bound<&T>. I on the other hand am storing Bounds directly in lots of places in my codebase, and have code that wants to do things to mutate them relatively frequently. That said, nothing actually calls Bound::as_mut right now, so any code example would be necessarily contrived. The core of my argument is one of principle-of-least-surprise much more than an actual motivating use case.

@Waridley
Copy link

Waridley commented Sep 9, 2021

I'm also using Bound directly a lot in my code, but since currently all of the types I'm using it with are Copy, I'm always just replacing the Bound itself instead of mutating the inner value anyway. However, I could imagine a scenario where someone were to use Bound<String>, Bound<Vec<u8>>, or some other large [Partial]Ord + !Copy type and map_mut could become useful. However, since map_mut isn't currently part of #86026, maybe as_mut().map(...) could be a solution in that case?

@lopopolo
Copy link
Contributor

I'm implementing my own Range type with Bound and would love to at least have Bound::as_ref to simplify the trait implementation for RangeBounds<usize>:

impl RangeBounds<usize> for Region {
    fn start_bound(&self) -> Bound<&usize> {
        // TODO: Use `self.start.as_ref()` when upstream `std` stabilizes:
        // https://github.com/rust-lang/rust/issues/80996
        match self.start {
            Bound::Included(ref bound) => Bound::Included(bound),
            Bound::Excluded(ref bound) => Bound::Excluded(bound),
            Bound::Unbounded => Bound::Unbounded,
        }
    }

    fn end_bound(&self) -> Bound<&usize> {
        // TODO: Use `self.end.as_ref()` when upstream `std` stabilizes:
        // https://github.com/rust-lang/rust/issues/80996
        match self.end {
            Bound::Included(ref bound) => Bound::Included(bound),
            Bound::Excluded(ref bound) => Bound::Excluded(bound),
            Bound::Unbounded => Bound::Unbounded,
        }
    }
}

lopopolo added a commit to artichoke/artichoke that referenced this issue Jul 23, 2022
Collapse a couple verbose `match` expressions to use the `Bound::cloned`
API which was stabilized in Rust 1.55.0.

Add a note and track the stabilization of rust-lang/rust#80996 to
replace more `match` expressions with `Bound::as_ref`.
@lopopolo
Copy link
Contributor

Would it be possible to split this tracking issue into two features, propose bound_as_ref for stabilization, and defer bound_as_mut for more discussion?

@yaahc
Copy link
Member

yaahc commented Jul 25, 2022

Basically any time I think about this I net out at preferring API consistency over all else.

Although consistency is a significant design consideration in many situations, I don't believe it is sufficient justification to add APIs on its own. We've updated our API proposal process since this API was added1, and the template that goes along with it includes a mandatory section for motivating examples. This should hopefully show how important real-world motivating use cases are to us. Otherwise, we'll have a ton of pretty methods that nobody uses and that serve only as clutter in the documentation. I advise sticking with David's original recommendation and stabilizing as_ref while removing as_mut until a compelling use case emerges.

Would it be possible to split this tracking issue into two features, propose bound_as_ref for stabilization, and defer bound_as_mut for more discussion?

I'd just skip straight to a partial stabilization2 personally.

Footnotes

  1. https://std-dev-guide.rust-lang.org/feature-lifecycle/api-change-proposals.html

  2. https://std-dev-guide.rust-lang.org/feature-lifecycle/stabilization.html#partial-stabilizations

@lopopolo
Copy link
Contributor

@yaahc I put up a partial stabilization PR here:

Dylan-DPC added a commit to Dylan-DPC/rust that referenced this issue Sep 3, 2022
…partial-stabilization-bounds-as-ref, r=dtolnay

Partially stabilize `bound_as_ref` by stabilizing `Bound::as_ref`

Stabilizing `Bound::as_ref` will simplify the implementation for `RangeBounds<usize>` for custom range types:

```rust
impl RangeBounds<usize> for Region {
    fn start_bound(&self) -> Bound<&usize> {
        // TODO: Use `self.start.as_ref()` when upstream `std` stabilizes:
        // rust-lang#80996
        match self.start {
            Bound::Included(ref bound) => Bound::Included(bound),
            Bound::Excluded(ref bound) => Bound::Excluded(bound),
            Bound::Unbounded => Bound::Unbounded,
        }
    }

    fn end_bound(&self) -> Bound<&usize> {
        // TODO: Use `self.end.as_ref()` when upstream `std` stabilizes:
        // rust-lang#80996
        match self.end {
            Bound::Included(ref bound) => Bound::Included(bound),
            Bound::Excluded(ref bound) => Bound::Excluded(bound),
            Bound::Unbounded => Bound::Unbounded,
        }
    }
}
```

See:

- rust-lang#80996
- rust-lang#80996 (comment)

cc `@yaahc` who suggested partial stabilization.
workingjubilee pushed a commit to tcdi/postgrestd that referenced this issue Sep 15, 2022
workingjubilee pushed a commit to tcdi/postgrestd that referenced this issue Sep 15, 2022
…tion-bounds-as-ref, r=dtolnay

Partially stabilize `bound_as_ref` by stabilizing `Bound::as_ref`

Stabilizing `Bound::as_ref` will simplify the implementation for `RangeBounds<usize>` for custom range types:

```rust
impl RangeBounds<usize> for Region {
    fn start_bound(&self) -> Bound<&usize> {
        // TODO: Use `self.start.as_ref()` when upstream `std` stabilizes:
        // rust-lang/rust#80996
        match self.start {
            Bound::Included(ref bound) => Bound::Included(bound),
            Bound::Excluded(ref bound) => Bound::Excluded(bound),
            Bound::Unbounded => Bound::Unbounded,
        }
    }

    fn end_bound(&self) -> Bound<&usize> {
        // TODO: Use `self.end.as_ref()` when upstream `std` stabilizes:
        // rust-lang/rust#80996
        match self.end {
            Bound::Included(ref bound) => Bound::Included(bound),
            Bound::Excluded(ref bound) => Bound::Excluded(bound),
            Bound::Unbounded => Bound::Unbounded,
        }
    }
}
```

See:

- #80996
- rust-lang/rust#80996 (comment)

cc `@yaahc` who suggested partial stabilization.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

9 participants