How can I mutate fields of a struct while referencing other fields? - rust

Here's an example of a problem I ran into:
pub struct Item {
name: String,
value: LockableValue, // another struct that I'd like to mutate
}
impl Item {
pub fn name(&self) -> &str {
&self.name
}
pub fn value_mut(&mut self) -> &mut LockableValue {
&self.value
}
}
pub fn update(item: &mut Item) {
let value = item.value_mut();
value.change(); // how it changes is unimportant
println!("Updated item: {}", item.name());
}
Now, I know why this fails. I have a mutable reference to item through the mutable reference to the value.
If I convert the reference to an owned String, it works fine, but looks strange to me:
pub fn update(item: &mut Item) {
let name = { item.name().to_owned() };
let value = item.value_mut();
value.change(); // how it changes is unimportant
println!("Updated item: {}", name); // It works!
}
If I let value reference drop, then everything is fine.
pub fn update(item: &mut Item) {
{
let value = item.value_mut();
value.change(); // how it changes is unimportant
}
println!("Updated item: {}", item.name()); // It works!
}
The value.change() block is rather large, and accessing other fields in item might be helpful. So while I do have solutions to this issue, I'm wondering if there is a better (code-smell) way to do this. Any suggestions?
My intention behind the above structs was to allow Items to change values, but the name should be immutable. LockableValue is an tool to interface with another memory system, and copying/cloning the struct is not a good idea, as the memory is managed there. (I implement Drop on LockableValue to clean up.)
I was hoping it would be straight-forward to protect members of the struct from modification (even if it were immutable) like this... and I can, but it ends up looking weird to me. Maybe I just need to get used to it?

You could use interior mutability on only the part that you want to mutate by using a RefCell like ths:
use std::cell::{RefCell, RefMut};
pub struct LockableValue;
impl LockableValue {
fn change(&mut self) {}
}
pub struct Item {
name: String,
value: RefCell<LockableValue>, // another struct that I'd like to mutate
}
impl Item {
pub fn name(&self) -> &str {
&self.name
}
pub fn value_mut(&self) -> RefMut<'_, LockableValue> {
self.value.borrow_mut()
}
}
pub fn update(item: &Item) {
let name = item.name();
let mut value = item.value_mut();
value.change(); // how it changes is unimportant
println!("Updated item: {}", name);
}
That way you only need a shared reference to Item and you don't run into an issue with the borrow checker.
Not that this forces the borrow checks on value to be done at runtime though and thus comes with a performance hit.

Related

Altering Lookup Value inplace

I am currently struggling to find a way to mutate a value of a LookupMap inplace. Here is a small example that shows what I am trying to achieve.
// Aux structure
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct Record {
pub i: usize,
}
impl Record {
pub fn inc(&mut self) {
self.i += 1;
}
}
// Main Contract
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct Welcome {
pub records: LookupMap<String, Record>,
}
impl Default for Welcome {
fn default() -> Self {
Self {
records: LookupMap::new(b"a".to_vec()),
}
}
}
#[near_bindgen]
impl Welcome {
pub fn add_element(&mut self, key: &str) {
self.records.insert(&key.to_string(), &Record{i: 0});
}
pub fn inc_element(&mut self, key: &str) {
self.records.get(&key.to_string()).unwrap().inc();
}
}
When I run the following test, it fails:
#[test]
fn test_inc() {
// This is the function from the examples.
let context = get_context(vec![], false);
testing_env!(context);
let mut contract = Welcome::default();
let key1 = "k";
contract.add_element(key1);
// This test passes
assert_eq!(contract.records.get(&key1.to_string()).unwrap().i, 0);
// This should increment the value of the record in place
contract.records.get(&key1.to_string()).unwrap().inc();
// This test FAILS!
assert_eq!(contract.records.get(&key1.to_string()).unwrap().i, 1);
}
I am struggling to understand how can I get a mutable reference, in order to change the Record that is stored as a value on the LookupMap in place. With an Hashmap, I can use the get_mut(key) method, but here it does not seem to be the case.
Not sure if I missed anything on the docs, as I have been looking into then for a while.
Thank you!
There might be a better way to do this but the following change makes the test pass, replace:
contract.records.get(&key1.to_string()).unwrap().inc();
with:
let mut record = contract.records.get(&key1.to_string()).unwrap_or_else(|| env::panic(b"Record not found."));
record.inc();
contract.records.insert(&key1.to_string(), &record);
I believe that because LookupMap stores its data on the NEAR blockchain you have go through the insert method to ensure that the data is persisted.

Rust struct within struct: borrowing, lifetime, generic types and more total confusion

I'm trying to modify an existing application that forces me to learn rust and it's giving me a hard time (reformulating...)
I would like to have a struct with two fields:
pub struct Something<'a> {
pkt_wtr: PacketWriter<&'a mut Vec<u8>>,
buf: Vec<u8>,
}
Where 'buf' will be used as an io for PacketWriter to write its results. So PacketWriter is something like
use std::io::{self};
pub struct PacketWriter<T :io::Write> {
wtr :T,
}
impl <T :io::Write> PacketWriter<T> {
pub fn new(wtr :T) -> Self {
return PacketWriter {
wtr,
};
}
pub fn into_inner(self) -> T {
self.wtr
}
pub fn write(&mut self) {
self.wtr.write_all(&[10,11,12]).unwrap();
println!("wrote packet");
}
}
Then inside 'Something' I want to use PacketWriter this way: let it write what it needs in 'buf' and drain it by pieces.
impl Something<'_> {
pub fn process(&mut self) {
self.pkt_wtr.write();
let c = self.buf.drain(0..1);
}
}
What seems to be impossible is to create a workable constructor for 'Something'
impl Something<'_> {
pub fn new() -> Self {
let mut buf = Vec::new();
let pkt_wtr = PacketWriter::new(&mut buf);
return Something {
pkt_wtr: pkt_wtr,
buf: buf,
};
}
}
What does not seem to be doable is, however I try, to have PacketWriter being constructed on a borrowed reference from 'buf' while 'buf' is also stored in the 'Something' object.
I can give 'buf' fully to 'PacketWriter' (per example below) but I cannot then access the content of 'buf' later. I know that it works in the example underneath, but it's because I can have access to the 'buf' after it is given to the "PacketWriter' (through 'wtr'). In reality, the 'PacketWriter' has that field (wtr) private and in addition it's a code that I cannot modify to, for example, obtain a getter for 'wtr'
Thanks
I wrote a small working program to describe the intent and the problem, with the two options
use std::io::{self};
pub struct PacketWriter<T :io::Write> {
wtr :T,
}
impl <T :io::Write> PacketWriter<T> {
pub fn new(wtr :T) -> Self {
return PacketWriter {
wtr,
};
}
pub fn into_inner(self) -> T {
self.wtr
}
pub fn write(&mut self) {
self.wtr.write_all(&[10,11,12]).unwrap();
println!("wrote packet");
}
}
/*
// that does not work of course because buf is local but this is not the issue
pub struct Something<'a> {
pkt_wtr: PacketWriter<&'a mut Vec<u8>>,
buf: Vec<u8>,
}
impl Something<'_> {
pub fn new() -> Self {
let mut buf = Vec::new();
let pkt_wtr = PacketWriter::new(&mut buf);
//let mut pkt_wtr = PacketWriter::new(buf);
return Something {
pkt_wtr,
buf,
};
}
pub fn process(&mut self) {
self.pkt_wtr.write();
println!("process {:?}", self.buf);
}
}
*/
pub struct Something {
pkt_wtr: PacketWriter<Vec<u8>>,
}
impl Something {
pub fn new() -> Self {
let pkt_wtr = PacketWriter::new(Vec::new());
return Something {
pkt_wtr,
};
}
pub fn process(&mut self) {
self.pkt_wtr.write();
let file = &mut self.pkt_wtr.wtr;
println!("processing Something {:?}", file);
let c = file.drain(0..1);
println!("Drained {:?}", c);
}
}
fn main() -> std::io::Result<()> {
let mut file = Vec::new();
let mut wtr = PacketWriter::new(&mut file);
wtr.write();
println!("Got data {:?}", file);
{
let c = file.drain(0..2);
println!("Drained {:?}", c);
}
println!("Remains {:?}", file);
let mut data = Something::new();
data.process();
Ok(())
}
It's not totally clear what the question is, given that the code appears to compile, but I can take a stab at one part: why can't you use into_inner() on self.wtr inside the process function?
into_inner takes ownership of the PacketWriter that gets passed into its self parameter. (You can tell this because the parameter is spelled self, rather than &self or &mut self.) Taking ownership means that it is consumed: it cannot be used anymore by the caller and the callee is responsible for dropping it (read: running destructors). After taking ownership of the PacketWriter, the into_inner function returns just the wtr field and drops (runs destructors on) the rest. But where does that leave the Something struct? It has a field that needs to contain a PacketWriter, and you just took its PacketWriter away and destroyed it! The function ends, and the value held in the PacketWriter field is unknown: it can't be thing that was in there from the beginning, because that was taken over by into_inner and destroyed. But it also can't be anything else.
Rust generally forbids structs from having uninitialized or undefined fields. You need to have that field defined at all times.
Here's the worked example:
pub fn process(&mut self) {
self.pkt_wtr.write();
// There's a valid PacketWriter in pkt_wtr
let raw_wtr: Vec<u8> = self.pkt_wtr.into_inner();
// The PacketWriter in pkt_wtr was consumed by into_inner!
// We have a raw_wtr of type Vec<u8>, but that's not the right type for pkt_wtr
// We could try to call this function here, but what would it do?
self.pkt_wtr.write();
println!("processing Something");
}
(Note: The example above has slightly squishy logic. Formally, because you don't own self, you can't do anything that would take ownership of any part of it, even if you put everything back neatly when you're done.)
You have a few options to fix this, but with one major caveat: with the public interface you have described, there is no way to get access to the PacketWriter::wtr field and put it back into the same PacketWriter. You'll have to extract the PacketWriter::wtr field and put it into a new PacketWriter.
Here's one way you could do it. Remember, the goal is to have self.packet_wtr defined at all times, so we'll use a function called mem::replace to put a dummy PacketWriter into self.pkt_wtr. This ensures that self.pkt_wtr always has something in it.
pub fn process(&mut self) {
self.pkt_wtr.write();
// Create a new dummy PacketWriter and swap it with self.pkt_wtr
// Returns an owned version of pkt_wtr that we're free to consume
let pkt_wtr_owned = std::mem::replace(&mut self.pkt_wtr, PacketWriter::new(Vec::new()));
// Consume pkt_wtr_owned, returning its wtr field
let raw_wtr = pkt_wtr_owned.into_inner();
// Do anything you want with raw_wtr here -- you own it.
println!("The vec is: {:?}", &raw_wtr);
// Create a new PacketWriter with the old PacketWriter's buffer.
// The dummy PacketWriter is dropped here.
self.pkt_wtr = PacketWriter::new(raw_wtr);
println!("processing Something");
}
Rust Playground
This solution is definitely a hack, and it's potentially a place where the borrow checker could be improved to realize that leaving a field temporarily undefined is fine, as long as it's not accessed before it is assigned again. (Though there may be an edge case I missed; this stuff is hard to reason about in general.) Additionally, this is the kind of thing that can be optimized away by later compiler passes through dead store elimination.
If this turns out to be a hotspot when profiling, there are unsafe techniques that would allow the field to be invalid for that period, but that would probably need a new question.
However, my recommendation would be to find a way to get an "escape hatch" function added to PacketWriter that lets you do exactly what you want to do: get a mutable reference to the inner wtr without taking ownership of PacketWriter.
impl<T: io::Write> PacketWriter<T> {
pub fn inner_mut(&mut self) -> &mut T {
&mut self.wtr
}
}
For clarification, I found a solution using Rc+RefCell or Arc+Mutex. I encapsulated the buffer in a Rc/RefCell and added a Write
pub struct WrappedWriter {
data :Arc<Mutex<Vec<u8>>>,
}
impl WrappedWriter {
pub fn new(data : Arc<Mutex<Vec<u8>>>) -> Self {
return WrappedWriter {
data,
};
}
}
impl Write for WrappedWriter {
fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
let mut data = self.data.lock().unwrap();
data.write(buf)
}
fn flush(&mut self) -> Result<(), Error> {
Ok(())
}
}
pub struct Something {
wtr: PacketWriter<WrappedWriter>,
data : Arc<Mutex<Vec<u8>>>,
}
impl Something {
pub fn new() -> Result<Self, Error> {
let data :Arc<Mutex<Vec<u8>>> = Arc::new(Mutex::new(Vec::new()));
let wtr = PacketWriter::new(WrappedWriter::new(Arc::clone(&data)));
return Ok(PassthroughDecoder {
wtr,
data,
});
}
pub fn process(&mut self) {
let mut data = self.data.lock().unwrap();
data.clear();
}
}
You can replace Arc by Rc and Mutex by RefCell if you don't have thread-safe issues in which case the reference access becomes
let data = self.data.borrow_mut();

Why RefCell's `into_inner` requires a move?

I have a situation where I have to move a struct from one object to another through a &mut self. Take a look:
pub struct MyStruct {
//no copy trait
device: Device
}
impl MyStruct {
pub fn finalize(&mut self) {
//error: cannot move since I borrowed
let interface = InterfaceBuilder::new(self.device)
}
}
First of all, why I cannot move something out of a borrowed mutable reference? Borrowed mutables are exclusive, there's no chance another code is looking into it.
Well, to address this problem I changed to
pub struct MyStruct {
//no copy trait
device: RefCell<Device>
}
impl MyStruct {
pub fn finalize(&mut self) {
//error on `self.device`: cannot move out of `self.device` which is behind a mutable reference
let interface = InterfaceBuilder::new(self.device.into_inner())
}
}
I know why the error occurs:
pub fn into_inner(self) -> T
calling into_inner makes self.device move. Why RefCell simply does not have an implementation pub fn into_inner(&mut self) -> T? I don't see a problem.
You cannot move out of a mutable reference because that would leave the original object incomplete.
Consider this code:
struct MyStruct {
s: String
}
fn finalize(f: &mut MyStruct) {
let _x = f.s; //error E0507!
}
fn main() {
let mut my = MyStruct {
s: "hi".into()
};
finalize(&mut my);
println!("{}", my.s); //what should this do?
}
Then, RefCell::into_inner(&mut self) -> T has the same problem. You could call it twice in a row and you would get two T values where before there was only one. And that, for a non Copy type is impossible.
If you want this function to consume the inner value, probably it should consume the outer value too:
fn finalize(f: MyStruct) {
let _x = f.s;
}
If you really want to move a value out of a mutable reference, you must leave something valid in its place. The easiest way is to declare an Option and use take() to steal and replace it with a None:
struct MyStruct {
s: Option<String>
}
fn finalize(f: &mut MyStruct) {
let _x = f.s.take();
}
Naturally, Option::take returns an Option so that if you call it twice, the second time you get None. If you are positive you have a value you can do take().uwnrap().
Alternatively, if your field type is Default you can use std::mem::take that replaces it with a default-created value:
struct MyStruct {
s: Vec<i32>
}
fn finalize(f: &mut MyStruct) {
let _x = std::mem::take(&mut f.s);
}
PS #1: there is Cell::take(&self) -> T, but only if T implements Default. It works just like std::mem::take but with a non-mutable reference.
PS #2: there is also unsafe fn ManuallyDrop::take(slot: &mut ManuallyDrop<T>) -> T, that is intented to be used in advanced drop implementations. But it is unsafe so it should never be your first option: if you call it twice you will get undefined behavior.

How to store struct reference in a vec and use the struct elsewhere later on?

I want to create a struct, store a reference to it in a vec, and then use the struct later on:
pub struct NonTerminal {
name: String
}
impl<'a> NonTerminal {
pub fn new(grammar: &'a mut Grammar, name: &str) -> &'a NonTerminal {
let t = NonTerminal {
name: name.to_string()
};
grammar.non_terminals.push(t);
grammar.non_terminals.last().unwrap()
}
}
pub struct Grammar<'a> {
non_terminals: Vec<NonTerminal>
}
pub fn create_rules<'a>(g: &'a mut grammar::Grammar<'a>) {
let sum = grammar::NonTerminal::new(g, "Sum");
let product = grammar::NonTerminal::new(g, "Product");
// this fails: "cannot borrow `*g` as mutable more than once at a time, second mutable borrow occurs here"
// ...
// use the sum and product vars when constructing rules
}
How to approach this?
You can't do this. This is simply the classical "vector push" example in disguise. Let's take a look at this simplified, but semantically equivalent code:
let mut v = Vec::new();
v.push(27);
let twenty_seven = v.last().unwrap();
v.push(3);
println!("{}", twenty_seven);
This does not compile and will never compile in Rust as this is inherently memory unsafe. You have a reference (a pointer) to an element in the vector. But the call to Vec::push might reallocate, invalidating all references to its elements. In C++ it would compile, but lead to UB because you would attempt to read uninitialized or unallocated memory.
The "answer" to your problem is... not simple. You have to think of another structure for your program. Sometimes, using reference counted smart pointers (like Rc) is useful. And it would easily solve your problem. But in many other situations you are better of completely rethinking your application.
Exactly one &mut borrow of data may occur at any given time.
I'd suggest inverting things so that Grammar has a function to add NonTerminals
pub struct NonTerminal {
name: String
}
impl NonTerminal {
pub fn new(name: &str) -> NonTerminal {
Self {
name: name.to_string()
}
}
}
pub struct Grammar {
pub non_terminals: Vec<NonTerminal>
}
impl Grammar {
fn add_non_terminal(&mut self, s: &str) -> &NonTerminal {
self.non_terminals.push(NonTerminal::new(s));
self.non_terminals.last().unwrap()
}
}
pub fn main() {
let mut g = Grammar {
non_terminals: vec![]
};
let product_ref = g.add_non_terminal("Product");
let sum_ref = g.add_non_terminal("Sum");
}
Updated based on feedback from #Lukas Kalbertodt.
Can iterate over all non-terminals via g.non_terminals

How do I efficiently build a vector and an index of that vector while processing a data stream?

I have a struct Foo:
struct Foo {
v: String,
// Other data not important for the question
}
I want to handle a data stream and save the result into Vec<Foo> and also create an index for this Vec<Foo> on the field Foo::v.
I want to use a HashMap<&str, usize> for the index, where the keys will be &Foo::v and the value is the position in the Vec<Foo>, but I'm open to other suggestions.
I want to do the data stream handling as fast as possible, which requires not doing obvious things twice.
For example, I want to:
allocate a String only once per one data stream reading
not search the index twice, once to check that the key does not exist, once for inserting new key.
not increase the run time by using Rc or RefCell.
The borrow checker does not allow this code:
let mut l = Vec::<Foo>::new();
{
let mut hash = HashMap::<&str, usize>::new();
//here is loop in real code, like:
//let mut s: String;
//while get_s(&mut s) {
let s = "aaa".to_string();
let idx: usize = match hash.entry(&s) { //a
Occupied(ent) => {
*ent.get()
}
Vacant(ent) => {
l.push(Foo { v: s }); //b
ent.insert(l.len() - 1);
l.len() - 1
}
};
// do something with idx
}
There are multiple problems:
hash.entry borrows the key so s must have a "bigger" lifetime than hash
I want to move s at line (b), while I have a read-only reference at line (a)
So how should I implement this simple algorithm without an extra call to String::clone or calling HashMap::get after calling HashMap::insert?
In general, what you are trying to accomplish is unsafe and Rust is correctly preventing you from doing something you shouldn't. For a simple example why, consider a Vec<u8>. If the vector has one item and a capacity of one, adding another value to the vector will cause a re-allocation and copying of all the values in the vector, invalidating any references into the vector. This would cause all of your keys in your index to point to arbitrary memory addresses, thus leading to unsafe behavior. The compiler prevents that.
In this case, there's two extra pieces of information that the compiler is unaware of but the programmer isn't:
There's an extra indirection — String is heap-allocated, so moving the pointer to that heap allocation isn't really a problem.
The String will never be changed. If it were, then it might reallocate, invalidating the referred-to address. Using a Box<[str]> instead of a String would be a way to enforce this via the type system.
In cases like this, it is OK to use unsafe code, so long as you properly document why it's not unsafe.
use std::collections::HashMap;
#[derive(Debug)]
struct Player {
name: String,
}
fn main() {
let names = ["alice", "bob", "clarice", "danny", "eustice", "frank"];
let mut players = Vec::new();
let mut index = HashMap::new();
for &name in &names {
let player = Player { name: name.into() };
let idx = players.len();
// I copied this code from Stack Overflow without reading the prose
// that describes why this unsafe block is actually safe
let stable_name: &str = unsafe { &*(player.name.as_str() as *const str) };
players.push(player);
index.insert(idx, stable_name);
}
for (k, v) in &index {
println!("{:?} -> {:?}", k, v);
}
for v in &players {
println!("{:?}", v);
}
}
However, my guess is that you don't want this code in your main method but want to return it from some function. That will be a problem, as you will quickly run into Why can't I store a value and a reference to that value in the same struct?.
Honestly, there's styles of code that don't fit well within Rust's limitations. If you run into these, you could:
decide that Rust isn't a good fit for you or your problem.
use unsafe code, preferably thoroughly tested and only exposing a safe API.
investigate alternate representations.
For example, I'd probably rewrite the code to have the index be the primary owner of the key:
use std::collections::BTreeMap;
#[derive(Debug)]
struct Player<'a> {
name: &'a str,
data: &'a PlayerData,
}
#[derive(Debug)]
struct PlayerData {
hit_points: u8,
}
#[derive(Debug)]
struct Players(BTreeMap<String, PlayerData>);
impl Players {
fn new<I>(iter: I) -> Self
where
I: IntoIterator,
I::Item: Into<String>,
{
let players = iter
.into_iter()
.map(|name| (name.into(), PlayerData { hit_points: 100 }))
.collect();
Players(players)
}
fn get<'a>(&'a self, name: &'a str) -> Option<Player<'a>> {
self.0.get(name).map(|data| Player { name, data })
}
}
fn main() {
let names = ["alice", "bob", "clarice", "danny", "eustice", "frank"];
let players = Players::new(names.iter().copied());
for (k, v) in &players.0 {
println!("{:?} -> {:?}", k, v);
}
println!("{:?}", players.get("eustice"));
}
Alternatively, as shown in What's the idiomatic way to make a lookup table which uses field of the item as the key?, you could wrap your type and store it in a set container instead:
use std::collections::BTreeSet;
#[derive(Debug, PartialEq, Eq)]
struct Player {
name: String,
hit_points: u8,
}
#[derive(Debug, Eq)]
struct PlayerByName(Player);
impl PlayerByName {
fn key(&self) -> &str {
&self.0.name
}
}
impl PartialOrd for PlayerByName {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for PlayerByName {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.key().cmp(&other.key())
}
}
impl PartialEq for PlayerByName {
fn eq(&self, other: &Self) -> bool {
self.key() == other.key()
}
}
impl std::borrow::Borrow<str> for PlayerByName {
fn borrow(&self) -> &str {
self.key()
}
}
#[derive(Debug)]
struct Players(BTreeSet<PlayerByName>);
impl Players {
fn new<I>(iter: I) -> Self
where
I: IntoIterator,
I::Item: Into<String>,
{
let players = iter
.into_iter()
.map(|name| {
PlayerByName(Player {
name: name.into(),
hit_points: 100,
})
})
.collect();
Players(players)
}
fn get(&self, name: &str) -> Option<&Player> {
self.0.get(name).map(|pbn| &pbn.0)
}
}
fn main() {
let names = ["alice", "bob", "clarice", "danny", "eustice", "frank"];
let players = Players::new(names.iter().copied());
for player in &players.0 {
println!("{:?}", player.0);
}
println!("{:?}", players.get("eustice"));
}
not increase the run time by using Rc or RefCell
Guessing about performance characteristics without performing profiling is never a good idea. I honestly don't believe that there'd be a noticeable performance loss from incrementing an integer when a value is cloned or dropped. If the problem required both an index and a vector, then I would reach for some kind of shared ownership.
not increase the run time by using Rc or RefCell.
#Shepmaster already demonstrated accomplishing this using unsafe, once you have I would encourage you to check how much Rc actually would cost you. Here is a full version with Rc:
use std::{
collections::{hash_map::Entry, HashMap},
rc::Rc,
};
#[derive(Debug)]
struct Foo {
v: Rc<str>,
}
#[derive(Debug)]
struct Collection {
vec: Vec<Foo>,
index: HashMap<Rc<str>, usize>,
}
impl Foo {
fn new(s: &str) -> Foo {
Foo {
v: s.into(),
}
}
}
impl Collection {
fn new() -> Collection {
Collection {
vec: Vec::new(),
index: HashMap::new(),
}
}
fn insert(&mut self, foo: Foo) {
match self.index.entry(foo.v.clone()) {
Entry::Occupied(o) => panic!(
"Duplicate entry for: {}, {:?} inserted before {:?}",
foo.v,
o.get(),
foo
),
Entry::Vacant(v) => v.insert(self.vec.len()),
};
self.vec.push(foo)
}
}
fn main() {
let mut collection = Collection::new();
for foo in vec![Foo::new("Hello"), Foo::new("World"), Foo::new("Go!")] {
collection.insert(foo)
}
println!("{:?}", collection);
}
The error is:
error: `s` does not live long enough
--> <anon>:27:5
|
16 | let idx: usize = match hash.entry(&s) { //a
| - borrow occurs here
...
27 | }
| ^ `s` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
The note: at the end is where the answer is.
s must outlive hash because you are using &s as a key in the HashMap. This reference will become invalid when s is dropped. But, as the note says, hash will be dropped after s. A quick fix is to swap the order of their declarations:
let s = "aaa".to_string();
let mut hash = HashMap::<&str, usize>::new();
But now you have another problem:
error[E0505]: cannot move out of `s` because it is borrowed
--> <anon>:22:33
|
17 | let idx: usize = match hash.entry(&s) { //a
| - borrow of `s` occurs here
...
22 | l.push(Foo { v: s }); //b
| ^ move out of `s` occurs here
This one is more obvious. s is borrowed by the Entry, which will live to the end of the block. Cloning s will fix that:
l.push(Foo { v: s.clone() }); //b
I only want to allocate s only once, not cloning it
But the type of Foo.v is String, so it will own its own copy of the str anyway. Just that type means you have to copy the s.
You can replace it with a &str instead which will allow it to stay as a reference into s:
struct Foo<'a> {
v: &'a str,
}
pub fn main() {
// s now lives longer than l
let s = "aaa".to_string();
let mut l = Vec::<Foo>::new();
{
let mut hash = HashMap::<&str, usize>::new();
let idx: usize = match hash.entry(&s) {
Occupied(ent) => {
*ent.get()
}
Vacant(ent) => {
l.push(Foo { v: &s });
ent.insert(l.len() - 1);
l.len() - 1
}
};
}
}
Note that, previously I had to move the declaration of s to before hash, so that it would outlive it. But now, l holds a reference to s, so it has to be declared even earlier, so that it outlives l.

Resources