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 Vec::resize_with and resize_default #41758

Closed
joshlf opened this issue May 4, 2017 · 53 comments · Fixed by #57002
Closed

Tracking issue for Vec::resize_with and resize_default #41758

joshlf opened this issue May 4, 2017 · 53 comments · Fixed by #57002
Labels
A-collections Area: `std::collection` B-unstable Blocker: Implemented in the nightly compiler and unstable. 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. Libs-Small Libs issues that are considered "small" or self-contained Libs-Tracked Libs issues that are tracked on the team's project board. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@joshlf
Copy link
Contributor

joshlf commented May 4, 2017

Currently, Vec has a resize method which takes a new parameter so that if the resize involves growing the vector, the new parameter is cloned into each newly-created cell in the vector. T must implement Clone in order for this method to be available.

It would be useful to add a resize_default method that instead requires that T: Default, and calls T::default() to fill the newly-created cells. Not only would this be ergonomic, but for certain implementations of T::default, it might allow the compiler to make better optimizations.

@clarfonthey
Copy link
Contributor

This would be an interesting addition for types which support Default but not Clone. I'll try and make a PR for it.

In the future, just FYI, feature requests should go in the RFCs repository.

@joshlf
Copy link
Contributor Author

joshlf commented May 5, 2017

Ah OK, noted. That's even for feature requests like this one that don't deserve an RFC?

@clarfonthey
Copy link
Contributor

Yep! The general rule is either PR here or RFC elsewhere.

Also, @joshlf would you mind renaming this issue to "Tracking issue for Vec::resize_default" ?

And @nikomatsakis would you mind tagging it?

@joshlf joshlf changed the title Add Vec::resize_default method Tracking issue for Vec::resize_default May 15, 2017
@joshlf
Copy link
Contributor Author

joshlf commented May 15, 2017

Gotcha; thanks!

frewsxcv added a commit to frewsxcv/rust that referenced this issue May 16, 2017
bors added a commit that referenced this issue May 16, 2017
Add Vec::resize_default.

As suggested by #41758.
@Mark-Simulacrum Mark-Simulacrum added B-unstable Blocker: Implemented in the nightly compiler and unstable. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Jun 22, 2017
@Mark-Simulacrum Mark-Simulacrum added the C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC label Jul 22, 2017
@Mark-Simulacrum
Copy link
Member

@rust-lang/libs Nominating for stabilization. The only potential concern is that we can generalize this to take a closure (see below). I don't know that it'd be all that useful, though, so I'm inclined to say that we shouldn't do so, and Clone and Default are the two "producing" traits in std, so standardizing around them seems logical.

fn resize_with(&mut self, new_len: usize, f: F)
where F: FnMut() -> T

@clarfonthey
Copy link
Contributor

@Mark-Simulacrum I'd be willing to make a PR that adds resize_with under a separate feature flag. I think that all three are useful; resize_default would simply be documented as equivalent to resize_with(Default::default)

@sfackler
Copy link
Member

I might lean more towards just having resize_with, for now at least. It's strictly more powerful and doesn't seem all that more verbose than resize_default?

@joshlf
Copy link
Contributor Author

joshlf commented Jan 18, 2018

My concern is that resize_with hampers discoverability - you have to come across resize_with and then connect the dots to realize that you can do resize_with(Default::default). Especially for beginners (and people who are ctrl+f-ing through the docs), this will probably make it less likely for people to figure out that they can resize using the default value.

@djc
Copy link
Contributor

djc commented Jan 18, 2018

Discoverability is pretty easily fixed by just having some documentation, though, right?

I like the power of resize_with, but I'm wondering about optimizability. Especially for Copy types (I guess this might depend on specialization), couldn't a resize_default-like API result in much faster code over something that requires calling a closure?

@sfackler
Copy link
Member

sfackler commented Jan 18, 2018

You'd just use resize for Copy types, but I'd expect resize_with(Default::default) to be literally identical to resize_default. Calling a function isn't really any different from calling a closure.

@sfackler
Copy link
Member

The @rust-lang/libs team discussed this today and feel like resize_with would be preferable.

@djc
Copy link
Contributor

djc commented Apr 1, 2018

I've submitted #49559 implementing Vec::resize_with() in order to help move this forward.

djc added a commit to djc/rust that referenced this issue Apr 3, 2018
kennytm added a commit to kennytm/rust that referenced this issue Apr 4, 2018
Introduce Vec::resize_with method (see rust-lang#41758)

In rust-lang#41758, the libs team decided they preferred `Vec::resize_with` over `Vec::resize_default()`. Here is an implementation to get this moving forward.

I don't know what the removal process for `Vec::resize_default()` should be, so I've left it in place for now. Would be happy to follow up with its removal.
@Kerollmops
Copy link
Contributor

Kerollmops commented Jan 30, 2019

Isn't Vec::resize_default some kind of completion of the Option/Result::unwrap_or_default methods? Don't know if my comment really make sense.

@clarfonthey
Copy link
Contributor

@Kerollmops And resize_with is like unwrap_or_else. We're not sure if we need a default version.

@Kerollmops
Copy link
Contributor

So, if we deprecate the Vec::resize_default method, why not the Option/Result::unwrap_or_default() ones?
I am probably splitting hairs, but...

Centril added a commit to Centril/rust that referenced this issue Feb 20, 2019
…r=SimonSapin

Deprecate the unstable Vec::resize_default

As a way to either get additional feedback to stabilize or help move nightly users off it.

Tracking issue: rust-lang#41758 (comment)

r? @SimonSapin
Centril added a commit to Centril/rust that referenced this issue Feb 22, 2019
…r=SimonSapin

Deprecate the unstable Vec::resize_default

As a way to either get additional feedback to stabilize or help move nightly users off it.

Tracking issue: rust-lang#41758 (comment)

r? @SimonSapin
Centril added a commit to Centril/rust that referenced this issue Feb 22, 2019
…r=SimonSapin

Deprecate the unstable Vec::resize_default

As a way to either get additional feedback to stabilize or help move nightly users off it.

Tracking issue: rust-lang#41758 (comment)

r? @SimonSapin
@orenbenkiki
Copy link

If the matter is open to discussion: I find that writing foo.resize_with(size, Default::default) or even foo.resize_with(size, default) is too verbose and less communicative of the intent than writing the shorter and clearer foo.resize_default(size). As @Kerollmops pointed out, there's a reason we have an unwrap_or_default.

@ghost
Copy link

ghost commented Jun 11, 2019

Is there any reason not to stabilize this right now? Offering functions that do the same thing as another function but with fewer arguments is a very common thing, and it's zero-cost in Rust.

@jonas-schievink jonas-schievink added the A-collections Area: `std::collection` label Nov 26, 2019
@Lucretiel
Copy link
Contributor

Lucretiel commented Dec 12, 2019

I'd like to have resize_default. I'm about to use it in one of my projects, plus I think it's good to have pairity with the other or_default methods, like [Option|Result]::unwrap_or_default and Entry::or_default

EDIT: I forgot one of my favorites, mem::take (which is just mem::replace(thing, Default::default)

@Lokathor
Copy link
Contributor

I like resize_default, it's very clear and more newbie friendly.

@Timmmm
Copy link
Contributor

Timmmm commented Apr 1, 2020

use of deprecated item 'std::vec::Vec::::resize_default': This is moving towards being removed in favor of .resize_with(Default::default). If you disagree, please comment in the tracking issue.

I disagree! resize_default() is such a common operation it is nice to have a short way of writing it. And as @Lucretiel mentioned there are plenty of other convenience _default methods in Rust. What exactly is the problem with leaving it in?

Edit: When I say "leaving it in", I actually thought it was already stable (haven't compiled code yet). I discovered it through rust-analyzer's autocomplete, thought "resize_default? Yes that's exactly what I want". Some empirical evidence for @joshlf's discoverability argument. No way would autocomplete have suggested resize_with(Default::default) and honestly I'd have had to have googled how to do it if that was the only way. Or realistically I would have just done resize(..., 0) which is rubbish.

@KamilaBorowska
Copy link
Contributor

From a quick search on grep.app (https://grep.app/search?q=.resize_with&filter[lang][0]=Rust), I decided to categorize every line using resize_with.

Default parameter usages

data_buffer.resize_with(batch_size, T::T::default);
buf.resize_with(batch_size, || 0);
buf.resize_with(batch_size, || 0);
self.characters.resize_with(cell_count, || None);
self.buffer.resize_with(len, Default::default);
self.buffer.resize_with(len, Default::default);
self.buffer.resize_with(len, Default::default);
data.resize_with(len, || None);
this.resize_with(i + 1, || None);
table_entries.resize_with(msix_vectors as usize, Default::default);
pba_entries.resize_with(num_pba_entries, Default::default);
items.resize_with(capacity, Default::default);
empty_items.resize_with(self.items.len(), Vec::default);
handles.resize_with(widgets.len().saturating_sub(1), || DragHandle::new());
.resize_with(self.widgets.len().saturating_sub(1), || DragHandle::new());
detected.resize_with(DETECTED_SLOTS_NUM, || Mutex::new(HashSet::default()));
buffer.resize_with(width * height, Default::default);
palette.resize_with(palette_length * 3, Default::default);
buffer.resize_with(width * 3, Default::default);
self.v.resize_with((idx + 1).max(self.v.len()), || None);
v.resize_with(255, Default::default);
instances.resize_with(max_instances, || Mutex::new(None));
data.resize_with(size, T::default);
vals.resize_with(BUCKETS, Default::default);
.resize_with(fluid.num_particles(), || RwLock::new(Vec::new()))
.resize_with(fluid.num_particles(), || RwLock::new(Vec::new()))
.resize_with(boundary.num_particles(), || RwLock::new(Vec::new()))
pixels_resolved_this_stage.resize_with(n_workers, || Mutex::new(Vec::new()));
rtrees.resize_with((grid_width * grid_height) as usize, || RwLock::new(RTree::new());
handles.resize_with(INSTANCES_PER_RUN, || None);
self.cur_buffer.resize_with(cols * rows, Default::default);
.resize_with(self.cur_buffer.len(), Char::default);
self.0.resize_with(id, Default::default);
target_candidates.resize_with(test.targets(), Default::default);
res.resize_with(n, Point::identity);
vec.resize_with(Self::capacity(), Default::default);
v.resize_with(rounded_size, T::default);
.resize_with((self.width * self.height) as usize, GridStoreCell::default);
.resize_with((self.width * self.height) as usize, GridStoreCell::default);
self.0.resize_with(i + 1, Default::default);
buckets.resize_with(num_buckets, Default::default);
buckets.resize_with(num_buckets, Default::default);
texels.resize_with((w * h) as usize * pf.canals_len(), Default::default);
self.traces.resize_with(TRACE_SIZE, Trace::default);
hotplug_slots.resize_with(HOTPLUG_COUNT, HotPlugState::default);
d.resize_with(pair.0, Default::default);
vcpu_states.resize_with(usize::from(config.max_vcpus), VcpuState::default);
plaintext_buf.resize_with(ci.len(), u8::default);
notoken_packet.resize_with(1200, u8::default);
self.children.resize_with(elements_count, || Vec::new());
envs_with_walked_drivers.resize_with(expected_len, Assoc::new);
entries.resize_with(rows * cols, Default::default);
v.resize_with(size, Default::default);
vals.resize_with(BUCKETS, Default::default);
pixels.resize_with(capacity, Default::default);
enc.resize_with(enc.len() + octects_needed, Default::default);
inputs.resize_with(edge.to.1 + 1, || None);
ids.resize_with(maxlen, || None);
.resize_with(meta_count.0, Default::default);
self.lines.resize_with(rows.len(), Default::default);
out.resize_with(self.h.len(), || None::<Tensor>);
kvm.vec_events.resize_with(new_len, || None);
output.resize_with(output.capacity(), Default::default);
self.obj_vec.resize_with(self.num_world_tiles + 1, || None);
buf.resize_with(512, Default::default);
buf.resize_with(512, Default::default);
blob.resize_with(size as usize, Default::default);
self.threads.resize_with(tid + 1, Default::default);
enc_id_vec.resize_with(32, Default::default);
new_reserved.resize_with(new_len, || AtomicU32::new(0));
.resize_with(meta_count.0, Default::default);
self.children.resize_with(ENTRIES_PER_NODE, || None);
new_data.resize_with(row * new_stride, Default::default);
new_data.resize_with(row * new_stride, Default::default);
self.ifd_list.resize_with(ifd_num + 1, Default::default);
self.data.resize_with(new_size, Default::default);
col_mat.resize_with(sparse_len, || Arc::new(Mutex::new(vec![])));
prod.resize_with(lhs + 1, || Vec::new());
c::WSAEFAULT => buffer.resize_with(1 + (len as usize) / mem::size_of::<usize>(), Default::default),
chart.resize_with(toks.len() + 1, std::default::Default::default);
vals.resize_with(BUCKETS, Default::default);
in_out.resize_with(ct_len + AUTH_TAG_BYTES, || 0);
buf.resize_with(test_str.as_bytes().len(), Default::default);
quant_new_rec.resize_with(new_height, Default::default);
msgs.resize_with(n * MSG_SIZE, Default::default);

Non-default parameter

self.dirty.resize_with(cell_count, || true);
.resize_with((self.width * self.height) as usize, || value);
authorities.resize_with(30, || PeerId::random());
full_nodes.resize_with(30, || PeerId::random());
self.element_yoga_nodes.resize_with(elements_count, || unsafe { YGNodeNew() });
self.text_yoga_nodes.resize_with(texts_count, || unsafe { YGNodeNew() });
shards.resize_with(N_SHARDS, || (AccessQueue::default(), FastLock::new(Shard::new(shard_capacity))));
positions.resize_with(positions.len() + sub_niddle.len(), || { pos += 1; pos - 1 });
isf_data.passes.resize_with(isf.passes.len(), || ...); // https://github.com/nannou-org/nannou/blob/master/nannou_isf/src/pipeline.rs#L948-L958
self.tabs.resize_with(num_cols.0, || { let is_tabstop = index % INITIAL_TABSTOPS == 0; index += 1; is_tabstop });
self.inner.resize_with(new_len, f);
self.pointers.resize_with(stack + 1, || Pointer { start: last_end, end: last_end });
self.layouts.resize_with(texts_count, || TextLayoutState { font_size: 16., line_height: 20., single_line_width: 0., glyph_ids: Vec::new(), xs: Vec::new(), break_hints: Vec::new(), breaks: Vec::new() }});
new_args.resize_with(arg_count, || FlatArg::Default);
texts.resize_with(texts_count, || unsafe { (gpu.create_buffer(), 0, 0.) })
.resize_with(expected_len, || Subtype::underspecified(n("ddd_bit")).0);
*mgr += self.list.inner_mut().resize_with(len, |n| ListEntry::new(n, n == active));
self.pending_pushes.resize_with(WORK_GROUP_WIDTH as usize, || GpuPush { dir_id: [-1.0; 4] });
data.resize_with(length + 1, || rng.gen());
bufs.resize_with(capacity, || Mutex::new(Buf { status: BufStatus::Unused, data: vec![0; 1 << T::BLOCK_SIZE_LOG2 as usize] }));
data.resize_with(capacity + 1, MaybeUninit::uninit);
self.nodes.resize_with(1, || g.gen_leaf(0));
val.resize_with(x, || self.clone());
self.pending.resize_with(offset + 1, || Output { buffer: writer.buffer(), done: false });
alloc.resize_with(alloc_cell_w, || { let mut out = Vec::with_capacity(alloc_cell_w); out.resize(alloc_cell_w, None); out })
bnodes.resize_with(*id, BlankNode::default)
enc_id_vec.resize_with(32, Default::default);
self.data.resize_with(size, || 0);
v.resize_with(opt.k, || unreachable!());
v.resize_with(opt.k, || unreachable!());

No default implementation, but one could be derived

v.resize_with(nports as usize, || PortState::new());
self.entries.resize_with(capacity, || CacheEntry { items: Vec::new(), occupied: false });
fluid_fluid_contacts.resize_with(fluids.len(), || ParticlesContacts::new());
fluid_boundary_contacts.resize_with(fluids.len(), || ParticlesContacts::new());
boundary_boundary_contacts.resize_with(boundaries.len(), || ParticlesContacts::new());
v.resize_with(hub_desc.num_ports as usize, || super::PortState::new());
out.resize_with(r#in.len(), || MaybeUninit::<wasi::Event>::zeroed().assume_init());
out.resize_with(in_.len(), || { MaybeUninit::<wasi_unstable::Event>::zeroed().assume_init() })

UB

There was one usage from glutin crate that I'm pretty sure is an UB. After determining what is the type created by std::mem::zeroed, I noticed it's c_void, which shouldn't be constructed, and another approach should be used to have a zeroed allocation.

config_ids.resize_with(num_configs as usize, || std::mem::zeroed());

@lambda-fairy
Copy link
Contributor

lambda-fairy commented Apr 15, 2020

Thanks for the info @xfix.

If the majority of calls can use Default::default then that would support having a specialized method for that case.

@Shnatsel
Copy link
Member

Shnatsel commented Apr 15, 2020

For completeness I've scanned crates.io for uses of .resize_with, exact command used was rg --iglob '*.rs' -F '.resize_with'. Here are the results: https://pastebin.com/UgpzzQxT

At a glance the results seem to align with the ones posted above - roughly half of the invocations just use the default value. On the other hand, there are just 207 uses of resize_with across the entire ecosystem, so I'm not sure adding another method for a relatively niche use case is worth it.

@Shnatsel
Copy link
Member

For reference, unwrap_or_else occurs 24631 times and unwrap_or_default 6286 times.

@SoniEx2
Copy link
Contributor

SoniEx2 commented Apr 15, 2020

for reference, how many unwrap_or with the default value for the type (0, "", etc)? how many unwrap_or_else with Default::default? is it possible that the problem is elsewhere? (bad docs, bad book, we failed our users?)

@Shnatsel
Copy link
Member

Here are all occurrences of .resize* and .unwrap_or* methods for your perusal:
resize.gz unwrap_or.gz

My point is, since the use of .resize_with is very rare, I'm not convinced adding further sugar for it is worth it.

@Timmmm
Copy link
Contributor

Timmmm commented Jul 27, 2020

My point is, since the use of .resize_with is very rare, I'm not convinced adding further sugar for it is worth it.

I think the point is that there are several other _default() methods so it should be present for consistency and discoverability, even if it is rarely used.

I'm pretty sure i128 is very rarely used but it would be weird to omit it when u128 exists.

@KodrAus KodrAus added Libs-Small Libs issues that are considered "small" or self-contained Libs-Tracked Libs issues that are tracked on the team's project board. labels Jul 29, 2020
@zakarumych
Copy link
Contributor

resize_default can be specialized for Vec<{integer}> using AllocRef::grow_zeroed

@scottmcm
Copy link
Member

@zakarumych That's currently done for vec![0; N], so I suspect it could then be done for .resize(n, 0) without needing a separate method. (That said, it might be interesting to have a .resize_zeroed(n) or new_zeroed(n) or something once safe transmute can give a trait bound for which it's safe to initialize elements with zeros.)

@zakarumych
Copy link
Contributor

zakarumych commented Nov 19, 2020

@scottmcm In generic environment, with 'Vec<T>' where 'T: Default', you can use either '.resize_default()' or '.resize_with(Default::default())', the former can be specialized for integers and later not.

@joshtriplett
Copy link
Member

resize_with has been shipped in stable, and resize_default was deprecated and removed in favor of resize_with.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-collections Area: `std::collection` B-unstable Blocker: Implemented in the nightly compiler and unstable. 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. Libs-Small Libs issues that are considered "small" or self-contained Libs-Tracked Libs issues that are tracked on the team's project board. 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.