"expected struct `std::rc::Rc`, found reference" - how to convert? - rust

I tried to get a reference-counted Rc<Foo> from a hash map and put it into a different container (Vec<Foo>).
Thought this would work (by incrementing the reference count), but instead I got an "expected struct std::rc::Rc, found reference" error.
How do I convert an &Rc<Foo> to a Rc<Foo>?
More info:
struct Foo();
let mut foo : HashMap<usize, Rc<Foo>> = HashMap::new();
let mut bar : Vec<Rc<Foo>> = Vec::new();
foo.insert(0, Rc::new(Foo()));
if let Some(x) = foo.get(&0) {
bar.push(x); // expected struct `std::rc::Rc`, found reference
// note: expected type `std::rc::Rc<Foo>`
// found type `&std::rc::Rc<Foo>` rustc(E0308)
}
I get that the hash map returns a reference to the value it owns. But dereferencing it doesn't work: both if let Some(&x) and bar.push(*x); result in a "cannot move out of borrowed content".
Curiously, adding a type annotation changes the error to "cannot move out of an Rc":
let x : &Rc<Foo> = x;
bar.push(*x); // cannot move out of an `Rc` rustc(E0507)
I need to store a reference to the same object, and not to a copy, so I avoided the .clone() "escape hatch".

To convert an &Rc<Foo> -> Rc<Foo>, use Rc::clone(), which gives you an Rc object of your own, increasing the reference count under the hood:
let ref_to_rc: &Rc<Foo> = &Rc::new(Foo());
let new_rc: Rc<Foo> = Rc::clone(ref_to_rc);
rc.clone() is equivalent to Rc::clone(&rc), but idiomatic Rust uses the latter to make it clear that the code only increases the refcount, not performing a deep copy of the data like some other implementations of .clone() do. (Though in some scenarios involving traits you might need to revert to ref_to_rc.clone().)
The errors above were about Rust refusing to do the copy implicitly. Why is std::rc::Rc<> not Copy? has an explanation of why it behaves like that.

Related

Rust Rc<RefCell>::borrow_mut returns &mut Rc<RefCell<T>> instead of RefMut<T>

so I'm relatively new in Rust and I was trying to get something similair to a std::shared_ptr in C++. I decided to go with the Rc<RefCell> pattern.
I'm trying to get and modify the value of Rc<RefCell<i32>> but borrow_mut() keeps returning &mut Rc<RefCell<i32>> instead of MutRef<i32>
I'm working on 2 projects currently. In the first project test_mut is of type RefMut<i32>.
let mut test: Rc<RefCell<i32>> = Rc::new(RefCell::new(5));
let test_mut = test.borrow_mut();
But in my other project test_mut is of type &mut Rc<RefCell<i32>>.
WHYYY??
When I don't let the compiler deduct the type and replace the code with:
let mut test: Rc<RefCell<i32>> = Rc::new(RefCell::new(5));
let test_mut: RefMut<i32> = test.borrow_mut();
I get the following error:
mismatched types
expected struct `RefMut<'_, i32>`
found mutable reference `&mut Rc<RefCell<i32>>`
If anyone has any idea how I can prevent this, you would be my hero :)
Locke's comment is right.
Because of the method resolution rules, if you have use std::borrow::BorrowMut;, BorrowMut::borrow_mut will get called instead of RefCell::borrow_mut. Basically, trait methods on the current type (&mut RefCell) are checked before methods on the deref type (&Rc).
You can either remove the import, or call it as an associated function:
let test_mut = RefCell::borrow_mut(&test);
This isn't necessary here, but the equivalent code without automatic deref would be:
use std::ops::Deref;
let test_mut = RefCell::borrow_mut(Deref::deref(&test))

Rust `value borrowed here after move` when setting to Map

I want to put an object in a serde_json::Map where the key will be a value from inside that object.
use serde_json::{json, Map, Value};
fn main() {
let a = json!({
"x": "y"
});
let mut d: Map<String, Value> = Map::new();
d[&a["x"].to_string()] = a;
}
Error:
borrow of moved value: `a`
value borrowed here after moverustcE0382
main.rs(9, 30): value moved here
main.rs(4, 9): move occurs because `a` has type `Value`, which does not implement the `Copy` trait
Even if you solve this lifetime issue, the IndexMut implementation for Map cannot be used for inserting new elements into a map. It panics if the given key is not present.
Use the insert() method instead, which solves both problems:
d.insert(a["x"].to_string(), a);
Since the proper solution has already been answered, I am going to cover another perspective on this,
d[&a["x"].to_string()] = a;
Even if the mentioned bit panics when there is no entry against the passed index, what it is really saying is that, get me the value at index x and replace it with a, = is invoking Copy here, since a doesn't doesn't implement Copy trait the compiler is giving you error.
In the following case, a is simply moved, so no Copy is invoked.
d.insert(a["x"].to_string(), a);
Also, the equivalent in your case may be get or insert.
let entry = d.entry(a["x"].to_string()).or_insert(a);
To access the value from entry you just deref it as *entry

Lifetime problem with HashMap with HashMap references as values

(New to rust, FYI!)
So - I've been trying to grok the concept of lifetimes in Rust. I've read the documentation, and read some blogs and SO posts on the topic. But still not quite getting it (hence, possibly a bad title to the question).
I've got a particular problem I'm trying to figure out, which I've boiled down to this small sample code (tried to make as close to a working sample as I could):
use std::collections::HashMap;
// Imagine this is actually a more complex type:
type BigComplexType = i32;
fn some_expensive_computation() -> BigComplexType {
// Imagine this takes some arguments, and takes a long time to run
return 123;
}
fn construct() -> HashMap<i32, &'static HashMap<i32, BigComplexType>> {
let mut main = HashMap::new();
let mut nested = HashMap::new();
nested.insert(1111, some_expensive_computation());
nested.insert(2222, some_expensive_computation());
// ... and lots more inserts...
main.insert(10, &nested);
main.insert(20, &nested);
// ... and lots more inserts...
// Imagine a lot more other nested HashMaps to follow here
// let mut nested2 = ...
// ...
// main.insert(..., &nested2);
// ...
return main;
}
fn main() {
construct();
}
This example is a bit trivial - in the actual code, I'm creating much more complicated and deeper structures in the construct() function.
What I'm trying to do is to create some sort of cache that holds these pre-computed values, so that they can be easily and quickly accessed else in the code. Perhaps this can all just be done in some totally different way - but I figured there must be a way to do it this way.
But, rustc complains here, because nested only exists in construct(), and once we're out of the function, it ceases to exist, and thus all references are invalid (or, that's how I understand the problem).
I've tried to introduce a 'a lifetime to the construct() function, and use that lifetime on the nested HashMap below but no dice. Got some errors, and could never fully make it work. I've tried all sorts of variants of adding lifetime annotations, but no dice.
I have a feeling I'm just not grokking some aspect of the whole concept here. Can anyone help point me in the right direction?
(I did think about gathering the nested HashMap into a vector and returning along side the main hashmap - so that then the function is returning the main HashMap, plus a vector of the nested ones - thus, the lifetime would be guaranteed, I think - but I hit some other roadblocks trying that, and have a feeling I'm over-complicating things.)
For reference, this is the error I get in compiling the above:
error[E0515]: cannot return value referencing local variable `nested`
--> lifetime_return.rs:29:12
|
19 | main.insert(10, &nested);
| ------- `nested` is borrowed here
...
29 | return main;
| ^^^^ returns a value referencing data owned by the current function
error[E0515]: cannot return value referencing local variable `nested`
--> lifetime_return.rs:29:12
|
20 | main.insert(20, &nested);
| ------- `nested` is borrowed here
...
29 | return main;
| ^^^^ returns a value referencing data owned by the current function
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0515`.
Any/all help would be greatly appreciated! I tried to look for similar question, but couldn't spot one - perhaps I just didn't recognize some SO post as a duplicate, as I don't fully understand the lifetime model just yet.
Your intuition for why this doesn't work is correct: nested lives only inside construct, and you try to return references to it in the hashmap that live for longer than the function. Assuming that you don't want to clone the nested maps, presumably because they're very large, you can use Rc instead as a way to have trivially cloneable references to the nested maps that keep them alive for as long as necessary:
use std::collections::HashMap;
use std::rc::Rc;
type BigComplexType = i32;
fn some_expensive_computation() -> BigComplexType {
return 123;
}
fn construct() -> HashMap<i32, Rc<HashMap<i32, BigComplexType>>> {
let mut main = HashMap::new();
let mut nested = HashMap::new();
nested.insert(1111, some_expensive_computation());
nested.insert(2222, some_expensive_computation());
let nested_rc = Rc::new(nested);
main.insert(10, Rc::clone(&nested_rc));
main.insert(20, nested_rc); // can move the Rc for the last insert
let mut nested2 = HashMap::new();
nested2.insert(3333, some_expensive_computation());
nested2.insert(4444, some_expensive_computation());
let nested2_rc = Rc::new(nested2);
main.insert(30, Rc::clone(&nested2_rc));
main.insert(40, nested2_rc);
return main;
}
fn main() {
let map = construct();
println!("{}", map[&10][&1111]); // 123
}
Playground link

Why do I get "no method named push found for type Option" with a vector of vectors?

I tried to use a String vector inside another vector:
let example: Vec<Vec<String>> = Vec::new();
for _number in 1..10 {
let mut temp: Vec<String> = Vec::new();
example.push(temp);
}
I should have 10 empty String vectors inside my vector, but:
example.get(0).push(String::from("test"));
fails with
error[E0599]: no method named `push` found for type `std::option::Option<&std::vec::Vec<std::string::String>>` in the current scope
--> src/main.rs:9:20
|
9 | example.get(0).push(String::from("test"));
| ^^^^
Why does it fail? Is it even possible to have an vector "inception"?
I highly recommend reading the documentation of types and methods before you use them. At the very least, look at the function's signature. For slice::get:
pub fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output>
where
I: SliceIndex<[T]>,
While there's some generics happening here, the important part is that the return type is an Option. An Option<Vec> is not a Vec.
Refer back to The Rust Programming Language's chapter on enums for more information about enums, including Option and Result. If you wish to continue using the semantics of get, you will need to:
Switch to get_mut as you want to mutate the inner vector.
Make example mutable.
Handle the case where the indexed value is missing. Here I use an if let.
let mut example: Vec<_> = std::iter::repeat_with(Vec::new).take(10).collect();
if let Some(v) = example.get_mut(0) {
v.push(String::from("test"));
}
If you want to kill the program if the value is not present at the index, the shortest thing is to use the index syntax []:
example[0].push(String::from("test"));

get file information from DirEntry in a for loop

I am new to Rust. I am trying to build a JSON object where the keys are file names and the value is the file contents.
So far, I have:
use std::fs;
use std::io;
use std::env;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
fn main() {
make_json();
}
fn make_json() -> io::Result<()> {
let mut modules = HashMap::new();
let mut dir = env::current_dir().unwrap();
let mut read_dir = fs::read_dir(dir);
for entry in try!(read_dir) {
let entry = try!(entry);
let file_name = entry.path().file_name().unwrap().to_string_lossy();
modules.insert(file_name, "");
}
Ok(())
}
When I go to compile it, I get
src/main.rs:19:25: 19:37 error: borrowed value does not live long enough
src/main.rs:19 let file_name = entry.path().file_name().unwrap().to_string_lossy();
^~~~~~~~~~~~
note: in expansion of for loop expansion
src/main.rs:17:5: 21:6 note: expansion site
src/main.rs:13:38: 23:2 note: reference must be valid for the block suffix following statement 0 at 13:37...
src/main.rs:13 let mut modules = HashMap::new();
src/main.rs:14 let mut dir = env::current_dir().unwrap();
src/main.rs:15 let mut read_dir = fs::read_dir(dir);
src/main.rs:16
src/main.rs:17 for entry in try!(read_dir) {
src/main.rs:18 let entry = try!(entry);
...
src/main.rs:19:9: 19:77 note: ...but borrowed value is only valid for the statement at 19:8
src/main.rs:19 let file_name = entry.path().file_name().unwrap().to_string_lossy();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:19:9: 19:77 help: consider using a `let` binding to increase its lifetime
src/main.rs:19 let file_name = entry.path().file_name().unwrap().to_string_lossy();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
I understand what this error is telling me; entry is defined within the scope of the for loop, and therefore if I store it to the HashMap it will no longer be valid memory because the place in memory will have been freed already. I get that.
What I don't get, is how I access the the DirEntrys within read_dir without using some sort of closure, since I will need their information outside of whatever closure I retrieve them in.
Everything that I have come across hasn't been able to help me.
DirEntry.path() returns a PathBuf, which is 'static (i.e. it contains no non-static references and is a completely standalong object). It is where the problem lies.
PathBuf.file_name() returns Option<&OsStr>, a reference into that object, and OsStr.to_string_lossy() returns Cow<str>. Note with that last that it is not 'static; with the elided lifetimes reinstated, it’s fn to_string_lossy<'a>(&'a self) -> Cow<'a, str>. This is for efficiency, because if the path is legal UTF-8 then there’s no need to go creating an entirely new owned string (String), it can keep it as a string slice (&str). (Because that’s what Cow<'a, str> is: its variants, with generics filled in, are Owned(String) and Borrowed(&'a str).)
What you need in this location is to turn the Cow<str> into a String. This is accomplished with the into_owned method of Cow<T>.
That line of code thus becomes this:
let file_name = entry.path().file_name().unwrap().to_string_lossy().into_owned();
Problem while dealing with Rust file system forced me to create this rust library brown
While dealing with Rust fs and specially while working with loops, the main issue is that every thing return another thing and then we need to convert that thing.
We need something to flatten the items for us
My suggestion :: Do not do any calculations etc in a loop, it should just have function calls to a well tested library and just checking its results.

Resources