Rust: Asymmetry in indexing vector on left and right side of assignment - vector

Can someone explain the difference between &v[0] versus v[0] where v is a Vector? If I were to guess it is similar to "borrowing" versus "moving", however the following examples would suggest a different reason.
1)
You can access the vector items by referring to the index number:
let mut data = vec![5, 10, 15];
let first = &data[0];
println!("The first element is {}", first);
To change the value of a specific item, refer to the index number:
let mut data = vec![5, 10, 15];
data[0] = 15;
Why not &data[0] in example 2, and the opposite in example 1? Will data be moved if the type inside the vector did not implement Copy trait?
PS: I'm coming from C++ background.

The square bracket indexing syntax automatically derefs the result returned from the Index or IndexMut trait. They resolve to the value stored in the data structure, and you have to explicitly reference in order to get a reference instead.
This means your examples desugar to this
// needed for explicit `index` and `index_mut`
use std::ops::{Index, IndexMut};
let mut data = vec![5, 10, 15];
let first = &{ *data.index(0) }; // first has type `&i32`
println!("The first element is {}", first);
data[0] = 15; // assignment is always done through IndexMut
*data.index_mut(0) = 15;
let mut_ref: &mut i32 = &mut data[i]; // also desugars to IndexMut
let mut_ref: &mut i32 = data.index_mut(i);
let imm_ref: &i32 = &data[i]; // desugars to Index
let imm_ref: &i32 = data.index(i);
let val: i32 = data[i]; // desugars to dereferenced Index
let val: i32 = *data.index(i);
For your examples, which use integers (which are a Copy type meaning that they can still be used after moves), there's really no reason to take a reference instead of just doing data[i] in the first case.

Related

Does the key of a HashMap have to be a pointer in Rust?

I'm admittedly new to Rust. That being said, this doesn't make sense to me yet finding out why the behavior I'm seeing isn't what I expect seems like a good learning opportunity:
use std::iter::Enumerate;
use std::collections::HashMap;
impl Solution {
pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {
let mut numToI: HashMap<i32, usize> = HashMap::new();
for (i,v) in nums.iter().enumerate() {
let num: i32 = *v;
let complement: i32 = target - num;
if numToI.contains_key(complement) {
return vec![i as i32, numToI.get(complement) as i32];
} else {
numToI.insert(complement, i);
}
}
return vec![-1,-1];
}
}
Here I'm doing the simple question twoSum. I understand that nums.iter().enumerate() will return the values i and v, which are of type usize and a pointer to the element in nums (so in this case a reference to an i32), respectively. The thing I'm having trouble with is that although I specify numToI is a HashMap<i32, usize>, not HashMap<&i32, usize>, and I dereference to get the value of v with *v and assign the value to num, when I check if the HashMap numToI contains this i32 dereferenced value as a key, I get the error: expected &i32, found i32 on the call to contains_key. Why is this? Is it because the HashMap type always requires a pointer rather than a raw value, or is it due to an intricacy of Rust I'm not aware of? Shouldn't it expect a pointer for the key instead of a i32 if I had used HashMap<&i32, i32>?
More importantly, if this is due to a difference between Rust and C that has to do with the way borrowing etc. is used in Rust, where can I learn more about the intricacies of these differences?
contains_key takes a reference. It doesn't need to take ownership of the value to test with - it just needs to look at it temporarily.
Rust is complaining that you are passing in an i32 by value instead of a reference to it. It should tell you to borrow instead: numToI.contains_key(&complement).
That's the only issue with your code, really. HashMap keys don't need to be references, and it would be really inconvenient if they did.

Raw pointers w/o unsafe

To dereference a raw pointer, we must use unsafe code. What are the situations in which raw pointers can be used (and can be useful) in ways that don't involve using unsafe code at all?
Pretty much the only thing you can do is cast the raw pointer to an usize. You can use that to do stuff like getting the index in an array from a reference into the array.
fn get_index_in_array(array: &[i32], item: &i32) -> usize {
let array_ptr: *const i32 = array.as_ptr();
let item_ptr: *const i32 = item;
let distance = ((item_ptr as usize) - (array_ptr as usize)) / std::mem::size_of::<i32>();
assert!(distance < array.len());
distance
}
fn main() {
let arr = [1, 2, 3, 4];
println!("{}", get_index_in_array(&arr, &arr[2]));
}
2

How to properly initialize a vector in Rust, given that the vector will be immutable after the initialization?

What is a good way to initialize the vector, without the mut keyword, given that the vector will be immutable after the initialization?
For example:
// nums is a `i32` vector(Vec<i32>)
// here is to pad two 1s with both ends
let mut padded: Vec<i32> = vec![0; len_p];
padded[0] = 1;
padded[len_n + 1] = 1;
for (idx, &num) in nums.iter().enumerate(){
padded[idx + 1] = num;
}
// `padded` will be read-only/immutable ever since
Otherwise, to mark padded mut, simply to initialize it, seems to me a waste of immutability, since I cannot enforce it after the initialization.
A common idiom seen in Rust is to introduce a local scope for just this purpose.
let padded: Vec<i32> = {
let mut tmp: Vec<i32> = vec![0; len_p];
tmp[0] = 1;
tmp[len_n + 1] = 1;
for (idx, &num) in nums.iter().enumerate(){
tmp[idx + 1] = num;
}
tmp
};
Inside the nested scope, we have a local variable called tmp which is mutable. Then, when we get to the end of that scope, we pass ownership of that vector to the immutable variable padded. The compiler is free to (and likely will) optimize out any actual movement that's happening, and this will compile to something as efficient as what you wrote. But from the borrow checker's perspective, tmp is mutable and padded is immutable, as desired. Once tmp goes out of scope, there's no way to modify padded anymore.
Remember, the owner of a value always determines whether or not that value is mutable, so when you pass ownership to someone else, they're free to change the mutability of the value upon taking ownership.
There are 3 ways:
Most functional (FP) approach: Use collect
let v: Vec<i32> = std::iter::once(1).chain(
nums.iter().copied()
)
.collect();
Initialize in scope. Useful when you need some extra variables which would be cleaned up after this.
let v = {
let mut v: Vec<i32> = vec![0; len_p];
v[0] = 1;
v[len_n + 1] = 1;
for (&src, dst) in nums.iter().zip(v[1..].iter_mut()){
*dst = src;
}
v
}
Rebind name and move vector into it. Useful when initialization is clear and simple.
let mut v: Vec<i32> = vec![0; len_p];
v[0] = 1;
v[len_n + 1] = 1;
for (&src, dst) in nums.iter().zip(v[1..].iter_mut()){
*dst = src;
}
// Rebind to immutable variable
let v = v;
// Cannot mutate here

If I want to code in Rust securely, should I code without using pointer arithmetic?

I've read that pointer arithmetic in Rust can be done through the pointer.offset() function, but it always has to be implemented in unsafe code:
fn main() {
let buf: [u32; 5] = [1, 2, 3, 4, 5];
let mut ptr1: *const u32 = buf.as_ptr();
unsafe {
let ptr2: *const u32 = buf.as_ptr().offset(buf.len() as isize);
while ptr1 < ptr2 {
println!("Address {:?} | Value {}", ptr1, *ptr1);
ptr1 = ptr1.offset(1);
}
}
}
If I want to code in Rust securely, should I code without using pointer arithmetic and just using the corresponding index of an array for example? Or is there any other way?
If I want to code in Rust securely
Then you should not use unsafe. There are a few legit reasons for unsafe (e.g. accessing memory locations that are known and safe to use, e.g. on microcontrollers several registers), but generally you should not use it.
should I code without using pointer arithmetic and just using the corresponding index of an array for example
Yes. There is no reason (in this specific case) to use unsafe at all. Just use
for i in 0..buf.len() {
println!("Value {}", buf[i]);
}
This code however is not considered as "rusty", instead use a for-loop
for i in &buf {
println!("Value {}", i);
}
Using raw pointers like that is very unlikely[1] to be faster than an idiomatic for loop over an iterator:
fn main() {
let buf: [u32; 5] = [1, 2, 3, 4, 5];
for val in buf.iter() {
println!("Address {:?} | Value {}", val as *const u32, val);
}
}
This is also much easier to read and doesn't introduce memory unsafety risks.
1 In fact, your code compares two pointer values each iteration, so is likely to be much slower than the idiomatic for loop, which can often omit all bounds checks.

How to allocate space for a Vec<T> in Rust?

I want to create a Vec<T> and make some room for it, but I don't know how to do it, and, to my surprise, there is almost nothing in the official documentation about this basic type.
let mut v: Vec<i32> = Vec<i32>(SIZE); // How do I do this ?
for i in 0..SIZE {
v[i] = i;
}
I know I can create an empty Vec<T> and fill it with pushes, but I don't want to do that since I don't always know, when writing a value at index i, if a value was already inserted there yet. I don't want to write, for obvious performance reasons, something like :
if i >= len(v) {
v.push(x);
} else {
v[i] = x;
}
And, of course, I can't use the vec! syntax either.
While vec![elem; count] from the accepted answer is sufficient to create a vector with all elements equal to the same value, there are other convenience functions.
Vec::with_capacity() creates a vector with the given capacity but with zero length. It means that until this capacity is reached, push() calls won't reallocate the vector, making push() essentially free:
fn main() {
let mut v = Vec::with_capacity(10);
for i in 0..10 {
v.push(i);
}
println!("{:?}", v);
}
You can also easily collect() a vector from an iterator. Example:
fn main() {
let v: Vec<_> = (1..10).collect();
println!("{:?}", v);
}
And finally, sometimes your vector contains values of primitive type and is supposed to be used as a buffer (e.g. in network communication). In this case you can use Vec::with_capacity() + set_len() unsafe method:
fn main() {
let mut v = Vec::with_capacity(10);
unsafe { v.set_len(10); }
for i in 0..10 {
v[i] = i;
}
println!("{:?}", v);
}
Note that you have to be extra careful if your vector contains values with destructors or references - it's easy to get a destructor run over a uninitialized piece of memory or to get an invalid reference this way. It will also work right if you only use initialized part of the vector (you have to track it yourself now). To read about all the possible dangers of uninitialized memory, you can read the documentation of mem::uninitialized().
You can use the first syntax of the vec! macro, specifically vec![elem; count]. For example:
vec![1; 10]
will create a Vec<_> containing 10 1s (the type _ will be determined later or default to i32). The elem given to the macro must implement Clone. The count can be a variable, too.
There is the Vec::resize method:
fn resize(&mut self, new_len: usize, value: T)
This code resizes an empty vector to 1024 elements by filling with the value 7:
let mut vec: Vec<i32> = Vec::new();
vec.resize(1024, 7);

Resources