How do I implement Deref + DerefMut properly? - rust

Hi I have the following this tutorial for learning more in depth what can I do with rust so I'm aware that this might now be the proper way to do thing. Here is the repository which contains all the code to take a in depth look. I'm having a global vga writer instance which needs to be safe for data races so using lazy_static I initialize it. What I want to do is is to be able to aquire the lock only when I actually print the string so that I implement fmt::Write on the WrappedWriter instead of the Writer to not obtain the lock over the spin::Mutex when I want to print a string rather I want to obtain the lock after the params were formatted . But when I call this the write function:
crate::vga_buffer::WRITER.write_fmt(args).unwrap();
I get the following error:
Compiling rust_os v0.1.0 (D:\Workspace\Organiztions\home\rust\rust_os)
error[E0055]: reached the recursion limit while auto-dereferencing `vga_buffer::writer::WrappedWriter`
--> src\lib.rs:19:31
|
19 | crate::vga_buffer::WRITER.write_fmt(args).unwrap();
| ^^^^^^^^^ deref recursion limit reached
|
= help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`rust_os`)
error: aborting due to previous error
For more information about this error, try `rustc --explain E0055`.
error: could not compile `rust_os`.
Here is the implementation of the Writer + WrappedWriter
use core::fmt;
use lazy_static::lazy_static;
use volatile::Volatile;
use super::color::*;
use super::screen_character;
use core::ops::{DerefMut, Deref};
// #[allow(dead_code)]
// use crate::serial_print;
// use crate::serial_println;
pub const BUFFER_HEIGHT: usize = 25;
pub const BUFFER_WIDTH: usize = 80;
#[repr(transparent)]
struct Buffer {
chars: [[Volatile<screen_character::ScreenCharacter>; BUFFER_WIDTH]; BUFFER_HEIGHT],
}
pub struct Writer {
column_position: usize,
color_code: ColorCode,
buffer: &'static mut Buffer,
}
impl Writer {
pub fn new(column_position: usize,
color_code: ColorCode) -> Writer {
return Writer {
column_position,
color_code,
buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
};
}
}
impl Writer {
pub fn write_byte(&mut self, byte: u8) {
match byte {
b'\n' => self.new_line(),
byte => {
if self.column_position >= BUFFER_WIDTH {
self.new_line();
}
let row = BUFFER_HEIGHT - 1;
let col = self.column_position;
let color_code = self.color_code;
self.buffer.chars[row][col].write(screen_character::ScreenCharacter::new(byte, color_code));
self.column_position += 1;
}
}
}
pub fn write_string(&mut self, s: &str) {
for byte in s.bytes() {
match byte {
// printable ASCII byte or newline
0x20..=0x7e | b'\n' => self.write_byte(byte),
// not part of printable ASCII range
_ => self.write_byte(0xfe),
}
}
}
fn new_line(&mut self) {
for row in 1..BUFFER_HEIGHT {
for col in 0..BUFFER_WIDTH {
let current_character = self.buffer.chars[row][col].read();
self.buffer.chars[row - 1][col].write(current_character);
}
}
self.clear_row(BUFFER_HEIGHT - 1);
self.column_position = 0;
}
fn clear_row(&mut self, row_index: usize) {
let blank = screen_character::ScreenCharacter::new(b' ', self.color_code);
for col in 0..BUFFER_WIDTH {
self.buffer.chars[row_index][col].write(blank);
}
}
}
// impl fmt::Write for Writer {
// fn write_str(&mut self, s: &str) -> fmt::Result {
// self.write_string(s);
// Ok(())
// }
// }
struct WrappedWriter {
value: spin::Mutex<Writer>
}
impl fmt::Write for WrappedWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.value.lock().write_string(s);
Ok(())
}
}
impl Deref for WrappedWriter {
type Target = WrappedWriter;
fn deref(&self) -> &Self::Target {
return self;
}
}
impl DerefMut for WrappedWriter {
fn deref_mut(&mut self) -> &mut Self::Target {
return self;
}
}
lazy_static! {
pub static ref WRITER: WrappedWriter = {
let writerInstance = WrappedWriter {
value: spin::Mutex::new(
Writer::new(0, ColorCode::new(Color::Yellow, Color::Black))
)
};
writerInstance
};
}
#[test_case]
fn test_println_output() {
let test_str = "Some test string that fits on a single line";
println!("{}", test_str);
for (char_index, char) in test_str.chars().enumerate() {
let screen_char = WRITER.value.lock().buffer.chars[BUFFER_HEIGHT - 2][char_index].read();
assert_eq!(char::from(screen_char.ascii_character), char);
}
}
Deref + DerefMut Were implemented because I get the following error without them but then again I didn't knew on what should I deref cause I don't actually need to deref from my point of view this are needed because write_fmt gets a mut of self
error[E0596]: cannot borrow data in a dereference of `vga_buffer::writer::WRITER` as mutable
--> src\lib.rs:19:5
|
19 | crate::vga_buffer::WRITER.write_fmt(args).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `vga_buffer::writer::WRITER`

WrappedWriter currently derefs to itself, which derefs to itself, which derefs to itself, and so on, which is why you reach the recursion limit. You probably want to make it deref to the Writer inside by obtaining a lock.

Related

Cannot return reference to temporary value with RwLock and iterators

I haven't found an answer to this in other questions.
I have reduced my problem to the following:
use std::sync::RwLock;
pub fn main() {
iter_lock().for_each(|v| {
println!("{}", v);
});
}
fn get_lock<'a>() -> &'a RwLock<Vec<u32>> {
static mut lock: RwLock<Vec<u32>> = RwLock::new(Vec::new());
unsafe { &lock }
}
fn iter_lock<'a>() -> impl std::iter::Iterator<Item = &'a u32> {
get_lock().read().unwrap().iter()
}
playground
The code above will not compile and give the following error:
error[E0515]: cannot return reference to temporary value
--> src/main.rs:15:5
|
15 | get_lock().read().unwrap().iter()
| --------------------------^^^^^^^
| |
| returns a reference to data owned by the current function
| temporary value created here
|
= help: use `.collect()` to allocate the iterator
Note that the static mut is not necessary in the code above, but I need it because I need to define a variable inside of an impl block.
I need to return an iterator, not a Vec because I am trying to avoid any allocations, and this function will always be used to iterate.
How can I solve this issue? I'm not afraid of using unsafe code, so unsafe suggestions are also welcome.
You can try something like this:
use std::sync::{RwLock, RwLockReadGuard};
pub fn main() {
let data = Data::new(&[1, 2, 3]);
data.iter().for_each(|x| println!("{:?}", x));
}
struct Data {
inner: RwLock<Vec<u32>>,
}
impl Data {
fn new(vec: &[u32]) -> Self {
Self {
inner: RwLock::new(vec.to_vec()),
}
}
fn iter(&self) -> Iter<'_> {
let d = self.inner.read().unwrap();
Iter::new(d)
}
}
struct Iter<'a> {
inner: RwLockReadGuard<'a, Vec<u32>>,
current_index: usize,
}
impl<'a> Iter<'a> {
pub fn new(inner: RwLockReadGuard<'a, Vec<u32>>) -> Iter<'a> {
Self {
inner,
current_index: 0,
}
}
}
impl Iterator for Iter<'_> {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
if self.current_index >= self.inner.len() {
return None;
}
let item = &self.inner[self.current_index];
self.current_index += 1;
Some(*item)
}
}

What is the idiomatic way to handle the actual data and not care about how the data is protected/wrapped?

This is valid code:
use std::rc::Rc;
use std::sync::{Arc, Mutex};
fn foo(n: i32) {
println!("n = {}", n)
}
fn main() {
let a = 1;
let b = Rc::new(2);
let c = Mutex::new(3);
let d = Arc::new(Mutex::new(4));
foo(a);
foo(*b);
foo(*(c.lock().unwrap()));
foo(*((*d).lock().unwrap()));
}
Are there any traits (or anything else) that I can implement so that the function calls could simply become:
foo(a);
foo(b);
foo(c);
foo(d);
What is the Rust idiomatic way for handling the actual data and not caring about how the data is protected/wrapped?
As others have pointed out, it is a bad idea to ignore the fallibility of Mutex::lock. However for the other cases, you can make your function accept owned values and references transparently by using Borrow:
use std::borrow::Borrow;
use std::rc::Rc;
fn foo (n: impl Borrow<i32>) {
println!("n = {}", n.borrow())
}
fn main() {
let a = 1;
let b = Rc::new (2);
let c = &3;
foo (a);
foo (b);
foo (c);
}
Playground
Here is an extremely literal answer to your specific question. I wouldn't use it.
use std::{
rc::Rc,
sync::{Arc, Mutex},
};
fn foo(n: impl DontCare<Output = i32>) {
let n = n.gimme_it();
println!("n = {}", n)
}
fn main() {
let a = 1;
let b = Rc::new(2);
let c = Mutex::new(3);
let d = Arc::new(Mutex::new(4));
foo(a);
foo(b);
foo(c);
foo(d);
}
trait DontCare {
type Output;
fn gimme_it(self) -> Self::Output;
}
impl DontCare for i32 {
type Output = Self;
fn gimme_it(self) -> Self::Output {
self
}
}
impl<T: DontCare> DontCare for Mutex<T> {
type Output = T::Output;
fn gimme_it(self) -> Self::Output {
self.into_inner()
.expect("Lets first assume unwrap() will not panic")
.gimme_it()
}
}
impl<T: DontCare> DontCare for Rc<T> {
type Output = T::Output;
fn gimme_it(self) -> Self::Output {
match Rc::try_unwrap(self) {
Ok(v) => v.gimme_it(),
_ => unreachable!("Lets first assume unwrap() will not panic"),
}
}
}
impl<T: DontCare> DontCare for Arc<T> {
type Output = T::Output;
fn gimme_it(self) -> Self::Output {
match Arc::try_unwrap(self) {
Ok(v) => v.gimme_it(),
_ => unreachable!("Lets first assume unwrap() will not panic"),
}
}
}
The function signatures you've specified take ownership of the value. That will be highly painful, especially paired with any type that doesn't implement Copy.
There are a number of code paths that panic implicitly. I'm not a fan of baking in panics — I reserve that for algorithmic failures, not data-driven ones.
See also:
Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?
My answer is similar to Shepmaster's answer, but maybe a little more practical since it doesn't consume the argument to foo2 and it gives you a reference to the target instead of taking it out of the container.
I never have any luck implementing traits based on other traits so I didn't try to do that here.
use std::ops::Deref;
use std::rc::Rc;
use std::sync::{Arc, Mutex, MutexGuard};
fn foo(n: i32) {
println!("n = {}", n)
}
trait Holding<T> {
type Holder: Deref<Target = T>;
fn held(self) -> Self::Holder;
}
fn foo2<H: Holding<i32>>(x: H) {
let h = x.held();
println!("x = {}", *h);
}
impl<'a, T: 'a> Holding<T> for &'a T {
type Holder = &'a T;
fn held(self) -> Self::Holder {
self
}
}
impl<'a, T> Holding<T> for &'a Rc<T> {
type Holder = &'a T;
fn held(self) -> Self::Holder {
&**self
}
}
impl<'a, T> Holding<T> for &'a Mutex<T> {
type Holder = MutexGuard<'a, T>;
fn held(self) -> Self::Holder {
// this can panic
self.lock().unwrap()
}
}
impl<'a, T> Holding<T> for &'a Arc<Mutex<T>> {
type Holder = MutexGuard<'a, T>;
fn held(self) -> Self::Holder {
// this can panic
(*self).lock().unwrap()
}
}
fn main() {
let a = 1;
let b = Rc::new(2);
let c = Mutex::new(3);
let d = Arc::new(Mutex::new(4));
foo(a);
foo(*b);
foo(*(c.lock().unwrap()));
foo(*((*d).lock().unwrap()));
foo2(&a);
foo2(&b);
foo2(&c);
foo2(&d);
}

How to implement a macro that defines a new public type and returns an instance of that type?

I want to implement a struct using macro_rules! because the generics require a lot of boilerplate and trait hunting.
The struct in question has a hash table inside but the key and the value types are to be provided by the user. The code is as follows:
macro_rules! new_ytz {
($T: ty) => {
// define the struct
pub struct Ytz {
table: hashbrown::hash_map::HashMap<$T, $T>,
}
impl Ytz {
pub fn new() -> Self {
Ytz {
table: hashbrown::hash_map::HashMap::<$T, $T>::new(),
}
}
pub fn add(&mut self, item: &$T) {
if self.table.contains_key(item) {
*self.table.get_mut(item).unwrap() += *item;
} else {
self.table.insert(*item, *item);
}
}
pub fn largest(&self) -> $T {
let mut result = 0;
for v in self.table.values() {
if result < *v {
result = *v;
}
}
result
}
}
// construct an instance of the struct and return it
Ytz::new()
};
}
// driver
fn main() {
let mut y = new_ytz!(u64); // should construct the object and return Ytz::new()
y.add(&71);
y.add(&25);
y.add(&25);
y.add(&25);
y.add(&34);
println!("{}", y.largest());
}
This won't compile since it tries to paste the struct within the main function:
error: expected expression, found keyword `pub`
--> src/main.rs:4:9
|
4 | pub struct Ytz {
| ^^^ expected expression
...
40 | let mut y = new_ytz!(u64); // should construct the object and return Ytz::new()
| ------------- in this macro invocation
How can I work around it? How can I paste the struct outside the main function publicly, along with the impl block?
generics require a lot of boilerplate
use std::collections::HashMap;
use core::hash::Hash;
use std::ops::AddAssign;
struct YtzU64<T: Eq + Ord + Hash + Copy + AddAssign> {
table: HashMap<T, T>
}
impl<T: Eq + Ord + Hash + Copy + AddAssign> YtzU64<T> {
pub fn new() -> Self {
Self {
table: HashMap::new()
}
}
pub fn add(&mut self, item: &T) {
if let Some(item) = self.table.get_mut(item) {
*item += *item;
} else {
self.table.insert(*item, *item);
}
}
pub fn largest(&self) -> Option<T> {
let mut values = self.table.values();
let mut largest:Option<T> = values.next().map(|t| *t);
for v in values {
if largest < Some(*v) {
largest = Some(*v);
}
}
largest
}
}
fn main() {
let mut y = YtzU64::new();
y.add(&71);
y.add(&25);
y.add(&25);
y.add(&25);
y.add(&34);
println!("{}", y.largest().unwrap());
}
My translation of your macro requires less boilerplate than your macro. It has two fewer indents, 4 fewer lines (macro_rules!, pattern matching at the top, two close braces at the end). Note that I changed the api slightly, as largest now returns an Option, to match std::iter::Iterator::max(). Also note that your api design is limited to T:Copy. You would have to redesign it a little if you want to support T: ?Copy + Clone or T: ?Copy + ?Clone.
trait hunting
The compiler is your friend. Watch what happens when I remove one of the trait bounds
error[E0277]: the trait bound `T: std::hash::Hash` is not satisfied
...
Using a macro is an interesting exercise, but re-implementing generics using macros is not useful.

What is the type/trait produced by calling the `chars` method on a string literal?

Consider this piece of code:
struct Collector<T>
where
T: Iterator<Item = char>,
{
current: u32,
right_neighbour: u32,
counter: usize,
iterator: T,
}
impl<T: Iterator<Item = char>> Collector<T> {
fn next(&self) -> u32 {
self.iterator
.next()
.expect("failed to get next digit!")
.to_digit(10)
.expect("failed to prase char as digit!")
}
fn collect(&self) {
self.current = self.right_neighbour;
self.right_neighbour = self.next();
self.counter = self.counter + 1;
}
fn initialize<U>(iterator: U) -> Collector<U>
where
U: Iterator<Item = char>,
{
let mut collector = Collector {
current: 0,
right_neighbour: 0,
counter: 0,
iterator: iterator,
};
collector.collect();
collector
}
}
fn main() {
let numstr = "1111";
let mut collector = Collector::initialize(numstr.chars().cycle().peekable());
}
It produces a type mismatch error:
error[E0284]: type annotations required: cannot resolve `<_ as std::iter::Iterator>::Item == char`
--> src/main.rs:46:25
|
46 | let mut collector = Collector::initialize(numstr.chars().cycle().peekable());
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: required by `<Collector<T>>::initialize`
What is the type of numstr.chars().cycle().peekable()? The compiler tells me that its full type is:
std::iter::Peekable<std::iter::Cycle<std::str::Chars<'_>>>
I know that I can't use that type in the definition of my structs/functions, because it doesn't have an explicit lifetime...
How can I correctly write this code?
What is the type/trait produced by calling the chars method on a string literal?
The documentation for str::chars tells you exactly what type it is:
fn chars(&self) -> Chars
It produces a type mismatch error
Yes, because you haven't specified what the concrete type of T should be. You've introduced a completely separate generic type U and have no arguments or return types that reference T. The compiler has zero context to use to infer what T is.
How can I correctly write this code?
Remove the useless extra type parameter:
struct Collector<T>
where
T: Iterator<Item = char>,
{
current: u32,
right_neighbour: u32,
counter: usize,
iterator: T,
}
impl<T: Iterator<Item = char>> Collector<T> {
fn new(iterator: T) -> Collector<T> {
let mut collector = Collector {
current: 0,
right_neighbour: 0,
counter: 0,
iterator: iterator,
};
collector.collect();
collector
}
fn collect(&self) {
unimplemented!()
}
fn next(&self) -> u32 {
unimplemented!()
}
}
fn main() {
let numstr = "1111";
let mut collector = Collector::new(numstr.chars().cycle().peekable());
}
I've removed the implementations of next and collect because they have other, unrelated errors that I don't care to fix. I also renamed initialize to new, as new is the standard constructor name in the absence of multiple constructors.
Of note is that the usage of peekable here is completely useless. The generic type T doesn't know that it's possible to call peek because there's no appropriate trait bound for such.
because it doesn't have an explicit lifetime
You don't have to care about the lifetime, that will be covered by the generic. If your type needed to know that it was peekable, just put that in your struct:
struct Collector<T>
where
T: Iterator<Item = char>,
{
// ...
iterator: std::iter::Peekable<T>,
}
impl<T: Iterator<Item = char>> Collector<T> {
fn new(iterator: T) -> Collector<T> {
let mut collector = Collector {
// ...
iterator: iterator.peekable(),
};
// ...
}
// ...
}
fn main() {
// ...
let mut collector = Collector::new(numstr.chars().cycle());
}

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