I generally find that the functions on std::vec::Vec are somewhat inconsistent. Before considering to propose any changes to this, I want to find out if people agree that it’s not quite perfect… Also, had there been a discussion of this already and whether DST may affect this in any significant way?
Is Vec
inconsistent to what? Vec
is one-of-a-kind type in Rust after all.
Being less vague by providing examples of the inconsistencies would help people provide feedback.
Apologies for being vague.
From the top down:
-
grow
- takes a slice… why it cannot take aVec
? -
tail
andtailn
- return a slice… why there is no version that would return aVec
? and how abouthead
andheadn
? -
last
- why there is nofirst
? -
push_all
- again, only takes a slice -
init
- don’t even understand why this function is needed and why it’s call so… and again it does return a slice, not aVec
Other counterintuitive naming choices are push
, unshitf
and append
…
From this it doesn’t seem like Vec
is an immutable sort of structure, I’d much rather like to see something close to Clojure, or otherwise something more minimal, like with Python list you only have: append
, count
, extend
, index
, insert
, pop
, remove
, reverse
, sort
- and that’s rather enough. I understand that Rust is not dynamic, growing would be an addition to the above, but otherwise I’m pretty confused by the set of methods a Vec
provides.
-
grow
doesn’t take a slice, it takes a reference. -
tail
andtailn
don’t returnVec
because they create a view into the last elements of a vector. To convert a slice to a vector, useslice.to_vec()
. - I don’t know why there is no
first
. (Something makes me think that this may have been removed recently.) That seems like it could be a reasonable addition, but it would be much more useful to have a method that returnsOption<&'a T>
for any index. -
push_all
takes a slice because slices are much more useful thanVec
s for pasing to functions. AnyVec
can be converted to a slice, but converting from a slice to aVec
can be an expensive operation, as it has to allocate. -
init
probably comes from languages like Haskell, where that is the standard name for such a method.init
,tail
etc. can be used quite a lot in functional programming to do with lists. It returns a slice for the same reasontail
does (see above).
All these methods are here to remove commonly-seen boilerplate code to do with lists. It’s significantly nicer to use std::os::args().tail()
to get the command-line arguments passed to your program than std::os::args().slice_from(1)
or even let args = std::os::args(); args.slice(1, args.len())
.
Eh, I feel like args().slice_from(1)
is almost better, since I can read it and know immediately what it does. tail
is more ambiguous.
Most of the methods you list there on Vec
are for ergonomic reasons, to avoid having to write the .as_slice
in some_vec.as_slice().foo()
so much (by putting the common ones directly on Vec
). With DST we can likely remove them and instead have a Deref<[T]> for Vec<T>
implementation, which will allow the &[T]
methods to be called automatically without being explicitly part of the Vec
API (although you may not regard this as a good thing).
Returning a Vec
from all those functions would be really really inefficient. Returning a slice is just returning a pointer into the memory owned by the Vec
, but returning a Vec
would require allocating new memory and cloning everything (or consuming the old Vec
and shifting pointers/byte-copying data around, which is still inefficient, especially for tail
). This slow behaviour copy-ful can be achieved with x.tail().to_vec()
(etc.).
first
is easily implemented as x[0]
(well, really, if/when we move get
to return an Option
, x.get(0)
); last
is more complicated/annoying/easier to get wrong: x.get(x.len() - 1)
.
In what way are push
and append
unintuitive?
There's .push_all_move
which takes a Vec
, both are somewhat useful, they can (and possibly should) be removed in favour of the more flexible .extend
method:
v.extend(slice.iter().map(|x| x.clone()));
v.extend(vec.move_iter())
We would still need some form of mutable Vec
. It's a equivalent to C++'s std::vector
(and can be more efficient due to Rust not having copy constructors!), with essentially the same functionality, as a (somewhat) general purpose sequence container that other structures can be built on (e.g. PriorityQueue
and (previously) HashMap
). An efficient immutable/persistent vector type is a different data structure, it is desirable but not a replacement for Vec
.
I agree that the Vec
API could use some adjustment/trimming (although I weakly recommend we wait until after DST before embarking in earnest), but having the 'flavourful' methods is nice, as you don't have to write (and debug) them when you do need them. And if necessary, they can be optimised using unsafe
code by the top Rust experts (with code-review by other top Rust experts); I think it's a reasonable statement that unsafe
code outside rust-lang/rust
is less likely to be correct, on average.
(Another approach we could/should investigate is seeing if we can turn some of the Vec
algorithms into more generic Iterator
ones, similar to C++.)
On Fri, Aug 01, 2014 at 10:27:38PM +0000, errordeveloper wrote:
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.