4 ms·
How would one make e.g. in Rust then ? #include <vector> int main() { std::vector<int> foo{1,2,3,4,5}; foo.reserve(foo.size()* 2); for(
by jcelerier 5y ago
How would one make e.g. in Rust then ?
#include <vector>
int main()
{
std::vector<int> foo{1,2,3,4,5};
foo.reserve(foo.size()* 2);
for(auto it = foo.begin(), end = foo.end(); it < end; ++it)
foo.push_back(*it);
}
- Lev1a 5y agoThe most short and elegant example I could come up with that actually worked (since the example above really shouldn't work in Rust in the first place): fn main() { let mut v = vec![1, 2, 3, 4, 5]; let new_size = v.len()*2; v = v.into_iter().cycle().take(new_size).collect(); println!("{:?}", v); } https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c5378b5ac3071f41219e1161097ac5cf https://play.rust-lang.org/?version=stable&mode=debug&editio...
- paavohtl 5y agoThere are a couple of options: - Repeating the vector using the built-in method `Vec::repeat`. This allocates a new vector, but `reserve` is likely to do the same, just implicitly. https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=49879aad21685ffa2d3c89b0a8e7145b https://play.rust-lang.org/?version=stable&mode=debug&editio... - Using a traditional for loop. There are no lifetime issues, because a new reference is acquired on each iteration. The reserve is not required, but I included it to match your C++ version. https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=30c4f5e4063dc797af04dbff3976c190 https://play.rust-lang.org/?version=stable&mode=debug&editio... - Creating an array of slices and then concatenating them into a new Vec using the concat method on slices: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2a11a101cb43daa640b56fc36417bcd1 https://play.rust-lang.org/?version=stable&mode=debug&editio...
- oconnor663 5y agoOther comments have answered this specific question, and I think it might be interesting to look at a similar-looking question that's actually more problematic for Rust. What I'll ask is, what's the Rust equivalent of this: #include <vector> void do_stuff(int &a, int &b) { // stuff } int main() { int my_array[2] = {42, 99}; do_stuff(my_array[0], my_array[1]); } That is, how do we take two non-aliasing mutable references into the same array/vector/view/span at the same time. (To be clear, none of the following applies to shared/const references. Those are allowed to alias, and this example will just work.) Notably, the direct translation doesn't compile: fn main() { let mut my_array = [42, 99]; do_stuff(&mut my_array[0], &mut my_array[1]); } Here's the error: error[E0499]: cannot borrow `my_array[_]` as mutable more than once at a time --> src/main.rs:7:32 | 7 | do_stuff(&mut my_array[0], &mut my_array[1]); | -------- ---------------- ^^^^^^^^^^^^^^^^ second mutable borrow occurs here | | | | | first mutable borrow occurs here | first borrow later used by call | = help: consider using `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices The issue here is partly that the compiler doesn't understand how indexing works. If it understood that my_array[0] and my_array[1] were disjoint objects, it could maybe deduce that this code was legal. But then the same error would come up again if one of the indexes was non-const, so adding compiler smarts here wouldn't help the general case. Getting multiple mutable references into a single container is tricky in Rust, because you (usually) have to statically guarantee that they don't alias, and how to do that depends on what container you're using. The suggestion in the error message is correct here, and `split_at_mut` is one of our options. Using it would look like this: fn main() { let mut my_array = [42, 99]; let (first_slice, second_slice) = my_array.split_at_mut(1); do_stuff(&mut first_slice[0], &mut second_slice[0]); } However, other containers like HashMap don't have `split_at_mut`, and taking two mutable references into e.g. a `HashMap<i32, i32>` would require a different approach. Refactoring our code to hold i32 keys instead of references would be the best option in that case, though it might mean paying for extra lookups. If we couldn't do that, we might have to resort to `Rc<RefCell<i32>>` or trickery with iterators. (This comment is too long already, and I'll spare the gory details.) At a high level, Rust's attitude towards multiple-mutable-references situations is leaning in the direction of "don't do that". There are definitely ways to do it (assuming what you're doing is in fact sound, and you're not actually trying to break the aliasing rule), but many of those ways are advanced and/or not-zero-cost, and in extremis it can require unsafe code. Life in Rust is a lot easier when you refactor things to avoid needing to do this, for example with an entity-component-system sort of architecture.
- devit 5y agoUse a crate that provides safe functions implemented with unsafe code to do that, like https://docs.rs/splitmut/0.2.1/splitmut/ https://docs.rs/splitmut/0.2.1/splitmut/
- oconnor663 5y agoNeat! I bet we could add a macro to that crate to make it work with any (static) number of references. A variant of this using a HashSet to check arbitrary collections of keys might be cool too.