Rust is memory safe. Did I break it?

Please have a look at this code:

fn main() {
    let mut v = vec!["this", "is", "a", "test"];
    println!("{:?}", v);
    
    // make a ref to an element in the vector 
    let ref_to_elem_in_v: &str = v[3];
    println!("{}", ref_to_elem_in_v);
    
    // clear and shrink v to 0 elements
    v.clear();
    v.shrink_to_fit();
    println!("{}", v.len());
    println!("{}", v.capacity());
    
    // we access the ref again to see what we get
    // and if it even compiles
    println!("{}", ref_to_elem_in_v);

    // oh .. it compiled. But isn't this unsafe?
    // is ref_to_elem_in_v not pointing to freed memory?
}

This is not a troll topic. I really would like to understand if this code is memory safe or not. Currently I believe it is unsafe. Can you help me understanding the issue? Thankyou!

I still :heart: Rust!

edit: spoiler: the code is memory safe, because ref_to_elem_in_v is not a reference to an element in v. it is a copy of the element in v :wink: study this post if you want further explanation: Rust is memory safe. Did I break it?

v contains &'static str values (pointers to strings), not the strings themselves. "a" is a static part of the program binary, not part of the vec that's destroyed when it's cleared.

5 Likes

To go of off what @sfackler said, change

let ref_to_elem_in_v: &str = v[3];

to

let ref_to_elem_in_v: &&str = &v[3];

To make it fail to compile

4 Likes

This is a copy of Vec's content, not a reference to the Vec:

let ref_to_elem_in_v: &str = v[3];

If you actually reference a vec's element, it won't compile:

   Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
  --> src/main.rs:10:5
   |
6  |     let ref_to_elem_in_v: &&str = &v[3];
   |                                    - immutable borrow occurs here
...
10 |     v.clear();
   |     ^^^^^^^^^ mutable borrow occurs here
...
17 |     println!("{}", ref_to_elem_in_v);
   |                    ---------------- immutable borrow later used here
3 Likes

Ah thankyou everyone! This forum seems to be so active it's more like a chat :smiley: :+1:

here is an example, which hopefully is more clear to other readers also interested in this:

fn main() {
    let mut v = vec![String::from("this"), String::from("is"), String::from("a"), String::from("test")];
    println!("{:?}", v);
    
    // make a ref to an element in the vector 
    let ref_to_elem_in_v: &String = &v[3];
    println!("{}", ref_to_elem_in_v);
    
    // clear and shrink v to 0 elements
    v.clear();
    v.shrink_to_fit();
    println!("{}", v.len());
    println!("{}", v.capacity());
    
    // we access the ref again to see what we get and if it even compiles
    println!("{}", ref_to_elem_in_v);

    // *sigh* it did not compile! yes! memory has been saved once again!
}

:heart: Rust

2 Likes

offtopic a bit, but...

I would like to see that v[i] is copying the value from v is clarified in the rust book. I commented on a closed issue here: https://github.com/rust-lang/book/issues/541

I post this here because the Issue seems to be quite old but wasn't handled back then ^^" I hope my post here is appropriate.

Can you explain why you would like to see that spelled out?

In every language I have ever used writing something like x = v[i] makes a copy of the ith element of v and assigns it to x.

Why would we assume otherwise for Rust?

Of course if v[i] is some complex type the compiler helpfully complains that it cannot copy it if it has no copy trait.

Is there something about Rust I'm missing here?

1 Like

I learned programming in Java. Currently I work with PHP for my part time job. With these two languages I would say I can program a real world application. I also got my hands on a bunch of other languages namely ECMA-Script/Javascript, Typescript, Python, C++, Lua, Lisp, Prolog...

To cut the long story short: in Java and PHP for example v[i] (or anything similar) would not make a copy but give a reference if v[i] is an instance of a compound type. That is the most common case when I am programming. v[i] will only return a copy of the contained value if v[i] is a scalar type. (Scalar types in rust are for example i32, f64, char, bool.)

I got used to programming with references to objects by using Java and PHP so much, that I was not aware of the fact that v[i] always returns a copy in Rust or it won't compile.

It makes sense in the context of Rust. I agree. But my experience with other languages lead me into false assumptions. That is why I would like to see this issue explained in the Rust beginners book.

I hope I could clarify your question. Thankyou for your input :smile:

edit: Ofcourse Java and PHP store references in collections. So one could argue that they are actually making copies of the contents of the collections, because what is stored inside is actually a reference and not the object. That is different for Rust, because in Rust we can actually store the objects inside the collections instead of pure references or pointers if we want to.

After all I still would like to see this fact spelled to clarify it for programmers used to other languages.

nuiun

As far as I can tell in all those language you mention x = v[i] results in x becoming a copy of what was in the ith element of the array v. Same in C++ and others.

If that happens to be a simple type like an integer then you get a copy of that integer.

If it happens to be a reference to a complex object then you get a copy of the reference to that complex object. The complex object itself is not copied.

The only difference I can see in Rust is that it is fussy about types. A thing is not the same as a reference to a thing (&thing).

This of course is spelled out in the early pages of the Rust Book and then spelled out further by the compiler. For example:

                  expected &s, found struct `s`
   |              help: consider borrowing here: `&v[0]`