Vector operations returning new vector instead of mutating in place


#1

Just out of curiousity, it seems like most kinds of operations done to the vectors (to certain extend String too) are in place instead of returning new a new instance. is there a way to always return a new instance for sorting, deduping etc.?

I found a way to concatenate vectors through [vec1, vec2].concat() but not much luck with the rest. Or is mutating in place a better way of getting things done in rust?


#2

You can use to_vec to copy clone the vector and then perform your operations on it.


#3

In Rust, ordinary vectors are values

FYI.


#4

but then… but … .but… then i can’t chain everything into a series of function calls /s

yea, just need to get used to this again, i suppose


#5

thanks, that is helpful (:smile:


#6

You haven’t exactly said what you want to do, but look into Iterators.


#7

actually just surprised to find out most operations involves mutating state in place, so am wondering if this is by design. For instance in other languages you can concatenate two lists to generate a new one, instead of changing one of them, or create a new string by concatenating two strings together, removing an element from list returns a new list without the element, and so on.

Also considering sometimes when in-place mutation happens it takes over the ownership if I am not careful, so it is a bit of a hassle.


#8

Concatenating of lists by copying is done via list1.into_iter().zip(list2).collect().

Rust by design has methods that modify in-place, because that’s often the simplest and the fastest way. Note that you can’t misuse them by accident — ownership and aliasing rules prevent such errors at compile time.

For collections the combination of shared+mutable data is problematic. Some languages avoid this problem by forbidding mutation (shared+immutable), but Rust can also solve this problem by forbidding sharing instead (owned+mutable).

For efficient immutable types, see https://github.com/bodil/im-rs


#9

It’s not necessarily better, but the mutating ones are often the core primitives because they can avoid allocations more often. (If you’re used to things like Java/C#, Rust’s String is more like C#'s StringBuilder – which is also preferred over repeatedly adding C# strings together because it can better amortize allocation costs.)

Another reason is that many things work on slices instead of mutable objects. For things like “I want to sort the first 10 elements of this vector”, it’s way easier to v[..10].sort_unstable() instead of splitting it, sorting one of them, and joining back together – in addition to needing no allocations instead of potentially 4 if all those operations made new copies.

You definitely want to check out iterators for most cases of “do stuff to a collection without modifying the original”, as it can often stream things together nicely and only .collect() into a collection at the end. For things like sorting, which aren’t offered streaming, consider things like IterTools::sorted that encapsulates the collect-then-sort-then-return-it steps.


#10

thanks for the help (: