YuriGeinishAlgn
YuriGeinishAlgn

Reputation: 45

What is the Rust way of keeping borrowed values in a collection?

In this:

#[derive(Debug)]
struct Person {
    name: String,
}

fn main() {
    let mut persons = Vec::<Person>::with_capacity(3);
    let mut personrefs = Vec::<&Person>::with_capacity(persons.capacity());
    for x in 0..persons.capacity() {
        let person = Person {
            name: format!("Alice-{}", x),
        };
        personrefs.push(&person);
        persons.push(person);
    }
    println!("persons are {:#?}", persons);
}

I sort of understand why Rust wouldn't allow pushing &person, but what is the Rust way of solving this?

I have a Vec that "owns" Persons, but I'd also like to map persons to some other data without touching the owning Vec or duplicating Persons in memory. Like, have a collections::HashMap<&Person, SomeOtherData>. If both HashMap and Vec have the same lifetime, wouldn't Rust know when to deallocate Persons?

Upvotes: 3

Views: 280

Answers (1)

Shepmaster
Shepmaster

Reputation: 431489

The "Rust way" is the way that doesn't lead to memory unsafety. Your code is invalid, as the compiler tells you:

error[E0597]: `person` does not live long enough
  --> src/main.rs:13:26
   |
13 |         personrefs.push(&person);
   |                          ^^^^^^ borrowed value does not live long enough
14 |         persons.push(person);
15 |     }
   |     - `person` dropped here while still borrowed
16 |     println!("persons are {:#?}", persons);
17 | }
   | - borrowed value needs to live until here

You are creating a Person inside the loop, taking a reference to it, then moving the Person to a new memory address, invalidating the reference. If you accessed that reference, you'd be touching undefined memory, leading to segfaults at best or "strange behavior" / security vulnerabilities at worst.

Likewise, you can't add the person to the Vec and then take a reference to that person in the loop because a subsequent iteration of the loop will mutate the vector. When you push to a vector, it might reallocate memory, again invalidating your references.

I'd write this code as

fn main() {
    let persons: Vec<_> = (0..3)
        .map(|x| Person {
            name: format!("Alice-{}", x),
        })
        .collect();

    let personrefs: Vec<_> = persons.iter().collect();
    println!("persons are {:#?}", persons);
}

Upvotes: 5

Related Questions