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 Option::get_or_insert_default #82901

Closed
2 of 4 tasks
camsteffen opened this issue Mar 8, 2021 · 25 comments · Fixed by #129087
Closed
2 of 4 tasks

Tracking Issue for Option::get_or_insert_default #82901

camsteffen opened this issue Mar 8, 2021 · 25 comments · Fixed by #129087
Labels
A-result-option Area: Result and Option combinators C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@camsteffen
Copy link
Contributor

camsteffen commented Mar 8, 2021

Feature gate: #![feature(option_get_or_insert_default)]

This is a tracking issue for adding Option::get_or_insert_default.

Basically, it is a shorthand for option.get_or_insert_with(Default::default). It differs with unwrap_or_default since it does not consume the Option. This is useful, for example, when you have an Option as a struct field or inside a data structure like Vec<Option<T>>.

Public API

impl<T> Option<T> {
    pub fn get_or_insert_default(&mut self) -> &mut T where T: Default;
}

Steps / History

Unresolved Questions

  • None yet.
@camsteffen camsteffen added C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Mar 8, 2021
@m-ou-se
Copy link
Member

m-ou-se commented Mar 9, 2021

It's not obvious to me at all from the name that this modifies the Option. It sounds like it'd be the same as unwrap_or_default. Something like get_or_insert_default would be more consistent with the other method names.

@m-ou-se m-ou-se added the A-result-option Area: Result and Option combinators label Mar 9, 2021
@camsteffen
Copy link
Contributor Author

Hmm I hadn't considered that.

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Mar 9, 2021
…=joshtriplett

Add Option::get_or_default

Tracking issue: rust-lang#82901

The original issue is rust-lang#55042, which was closed, but for an invalid reason (see discussion there). Opening this to reconsider (I hope that's okay). It seems like the only gap for `Option` being "entry-like".

I ran into a need for this method where I had a `Vec<Option<MyData>>` and wanted to do `vec[n].get_or_default().my_data_method()`. Using an `Option` as an inner component of a data structure is probably where the need for this will normally arise.
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Mar 9, 2021
…=joshtriplett

Add Option::get_or_default

Tracking issue: rust-lang#82901

The original issue is rust-lang#55042, which was closed, but for an invalid reason (see discussion there). Opening this to reconsider (I hope that's okay). It seems like the only gap for `Option` being "entry-like".

I ran into a need for this method where I had a `Vec<Option<MyData>>` and wanted to do `vec[n].get_or_default().my_data_method()`. Using an `Option` as an inner component of a data structure is probably where the need for this will normally arise.
JohnTitor added a commit to JohnTitor/rust that referenced this issue Mar 9, 2021
…=joshtriplett

Add Option::get_or_default

Tracking issue: rust-lang#82901

The original issue is rust-lang#55042, which was closed, but for an invalid reason (see discussion there). Opening this to reconsider (I hope that's okay). It seems like the only gap for `Option` being "entry-like".

I ran into a need for this method where I had a `Vec<Option<MyData>>` and wanted to do `vec[n].get_or_default().my_data_method()`. Using an `Option` as an inner component of a data structure is probably where the need for this will normally arise.
@camsteffen camsteffen changed the title Tracking Issue for Option::get_or_default Tracking Issue for Option::get_or_insert_default Mar 10, 2021
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Mar 10, 2021
…ou-se

Rename `Option::get_or_default` to `get_or_insert_default`

...as [suggested](rust-lang#82901 (comment)) by `@m-ou-se.` In hindsight this seems rather obvious, at least to me.

r? `@joshtriplett`
@sffc
Copy link

sffc commented Dec 31, 2021

+1 on the name get_or_insert_default

I wanted to use this today, but it's still experimental. 😆

@insou22
Copy link

insou22 commented Apr 7, 2022

+1 -- was looking for this exact method today

@djc
Copy link
Contributor

djc commented Jul 7, 2022

@rustbot ping libs

Let's see if this works now? Would be nice to have this FCP'ed.

cc @joshtriplett

@rustbot
Copy link
Collaborator

rustbot commented Jul 7, 2022

Error: Only Rust team members can ping teams.

Please file an issue on GitHub at triagebot if there's a problem with this bot, or reach out on #t-infra on Zulip.

@joshtriplett
Copy link
Member

LGTM

@rfcbot merge

@rfcbot
Copy link

rfcbot commented Jul 8, 2022

Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members:

Concerns:

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Jul 8, 2022
@rfcbot rfcbot added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. labels Sep 13, 2022
@rfcbot
Copy link

rfcbot commented Sep 13, 2022

🔔 This is now entering its final comment period, as per the review above. 🔔

@dtolnay
Copy link
Member

dtolnay commented Sep 13, 2022

@rfcbot concern is this in the same category as resize_default

Many years ago we deleted vec.resize_default(n) because it just wasn't worth having when vec.resize_with(n, T::default) is equally suitable.

It seems like it came down to just not being worth dedicating a whole new method given how rarely resize_with is used in the first place. This is in contrast to option.unwrap_or_default() as shorthand for option.unwrap_or_else(T::default), which is the only existing precedent for T::default getting a dedicated convenience that I know of, and is justified by being used literally over 100× more than resize_with.

I suppose option.get_or_insert_default() is much closer to the resize_with end of this spectrum than the unwrap_or_default end, and option.get_or_insert_with(T::default) would be good enough.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. labels Sep 13, 2022
@imp
Copy link

imp commented Oct 17, 2022

My own experience (which may or may not be representative) tells that in every single case I wanted option.get_or_insert_-like functionality I actually wanted option.get_or_insert_default(). So in my books it is much closer to unwrap_or_default than to resize_with. Just my 2 shekels.

@dtolnay
Copy link
Member

dtolnay commented Oct 17, 2022

Be aware that whether or not you want the default value is not the only consideration here. You also require a situation in which T is completely inferrable. The combination of those 2 is less common in my experience (especially if I also take into account experience from similar situations in the Entry API). Extrapolating "every single case I wanted the default" to "therefore I would have found get_or_insert_default valuable if available" is not correct.

use std::collections::BTreeSet;
use std::iter;

fn main() {
    let mut junks = None;
    for (id, value) in iter::repeat(("junk", 99)).take(10) {
        if id == "junk" {
            junks.get_or_insert_with(BTreeSet::default).insert(value);
        }
    }
    println!("{:?}", junks);
}

With get_or_insert_default:

error[E0282]: type annotations needed for `Option<T>`
 --> src/main.rs:5:9
  |
5 |     let mut junks = None;
  |         ^^^^^^^^^
...
8 |             junks.get_or_insert_default().insert(value);
  |                                           ------ type must be known at this point

@imp
Copy link

imp commented Oct 17, 2022

Very good example. However, in my opinion it doesn't lower the value of get_or_insert_default. Just my opinion - I realize others can feel differently.

@Rua
Copy link
Contributor

Rua commented Nov 5, 2022

Is there anything blocking stabilisation of this?

@SmnTin
Copy link

SmnTin commented Aug 24, 2023

What is the current status? Can we stabilize it?

@nyurik
Copy link
Contributor

nyurik commented Feb 22, 2024

Are there steps to move this forward and stabilize? I just encountered tons of these cases in PGRX - and was surprised it wasn't stable yet

@dtolnay
Copy link
Member

dtolnay commented Mar 23, 2024

My opinion remains #82901 (comment) and I would prefer not to grow the API of Option with this method. I think this is a frivolous wrapper around an existing good, usable method. The operation that this new method implements is not so widespread that it deserves a dedicated method on this foundational type, on top of the already available one. The writeability gain is minuscule or negative and the readability gain is minuscule or negative.

I am open to being overruled 4:1 if I'm wrong about this. Among the unchecked boxes in the FCP proposal, Jane has departed the team since then but I am interested to hear @BurntSushi's opinion (and if the other team members could reaffirm that they still like this method, since those approvals all happened before the critical comments — as did mine before I eventually changed my mind).

@BurntSushi
Copy link
Member

I think for me, Option (and Result) have become so big, that the marginal cost of another combinator has become rather small. And in this case, I perceived it as an extension of a pattern that we've already established with unwrap_or_default (which I understand you addressed by pointing out that it is exceptionally common) and Entry::or_default. What that means is that this isn't just a whole new method as it is relying on an established pattern that is pretty common. So the additional conceptual burden it brings seems somewhat small to me.

With that said, if most uses of this new routine require inference help, then I would agree that the juice does not seem worth squeeze. @nyurik in particular linked to a code sample where the Option type was already written down, which I think is probably the uncommon case. So I think one thing folks could do here is show more real world examples where this method would work without inference failures.

I would say my current opinion is "weakly in favor if inference failures don't happen in most cases." But if using this method just means that you'll likely need to write down the type anyway, then I think my position would be "not strongly opposed."

@dtolnay
Copy link
Member

dtolnay commented Mar 23, 2024

In the compiler, there are 28 calls of Option::get_or_insert_with and Option::get_or_insert_default currently. 79% need to stick with get_or_insert_with because they are inserting with something that is not the default value. (This is quite divergent from #82901 (comment). But it closely matches the ratio of unwrap_or_else to unwrap_or_default in the compiler: 83% of those calls are unwrap_or_else.)

Of the remainder, the 2 in compiler/rustc_session/src/options.rs and the 3 in compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs are inserting default into a struct field or function argument, where the type already needs to have been declared anyway.

And 1 in compiler/rustc_mir_transform/src/elaborate_box_derefs.rs uses get_or_insert_default but requires inference help.

     if let VarDebugInfoContents::Place(place) = &mut debug_info.value {
-        let mut new_projections = None;
+        let mut new_projections: Option<Vec<_>> = None;
         let mut last_deref = 0;

         for (i, (base, elem)) in place.iter_projections().enumerate() {
             let base_ty = base.ty(&body.local_decls, tcx).ty;

             if elem == PlaceElem::Deref && base_ty.is_box() {
-                let new_projections = new_projections.get_or_insert_with(Vec::new);
+                let new_projections = new_projections.get_or_insert_default();

                 let (unique_ty, nonnull_ty, ptr_ty) =
                     build_ptr_tys(tcx, base_ty.boxed_ty(), unique_did, nonnull_did);

@dtolnay
Copy link
Member

dtolnay commented Mar 23, 2024

In PGRX which was linked above as a motivating use case, there are 3 functions that call Option::get_or_insert_with using a default, of which 2 (here and here) would require adding a type to the declaration of the Option local variable in order to be able to use get_or_insert_default with it, while 1 (here) would not.

@camsteffen
Copy link
Contributor Author

would require adding a type to the declaration of the Option local variable in order to be able to use get_or_insert_default with it

My personal take on that scenario is that adding a type annotation to the variable is a readability win. I prefer to know/define the types of things upfront for clarity, and then value-modifying code is not responsible for providing type information to the reader or to the compiler.

@nyurik
Copy link
Contributor

nyurik commented Mar 27, 2024

I just did a quick github search for get_or_insert_with(Default, and there are 1.8k instances. I think this is fairly significant, but obviously this is not the most accurate metric. Doing some spot checks does appear they would benefit from this new feature. Note that the search result already implies known type -- e.g. it acts on a struct field or a method param.

@dtolnay
Copy link
Member

dtolnay commented Aug 6, 2024

I am not sure I will personally end up using get_or_insert_default, but I think there's been enough evidence above that this will be useful to people.

@rfcbot resolve is this in the same category as resize_default

@rfcbot rfcbot added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. labels Aug 6, 2024
@rfcbot
Copy link

rfcbot commented Aug 6, 2024

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot rfcbot added finished-final-comment-period The final comment period is finished for this PR / Issue. and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. labels Aug 16, 2024
@rfcbot
Copy link

rfcbot commented Aug 16, 2024

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

This will be merged soon.

@rfcbot rfcbot added the to-announce Announce this issue on triage meeting label Aug 16, 2024
@apiraino apiraino removed the to-announce Announce this issue on triage meeting label Aug 22, 2024
@bors bors closed this as completed in e76eb96 Sep 27, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Sep 27, 2024
Rollup merge of rust-lang#129087 - slanterns:option_get_or_insert_default, r=dtolnay

Stabilize `option_get_or_insert_default`

Closes: rust-lang#82901.

`@rustbot` label: +T-libs-api

r? libs-api
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-result-option Area: Result and Option combinators C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. 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.