This question already has answers here:
Why can't I store a value and a reference to that value in the same struct?
(4 answers)
Closed 6 months ago.
I'm trying to create a simple collection:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=361258962c9a25b953aab2a9e4999cc9
use std::collections::HashMap;
pub struct User {
pub id: u32,
pub name: String,
}
pub struct UsersIndex<'a> {
list: Vec<User>,
index: HashMap<u32, &'a User>,
}
impl UsersIndex<'_> {
pub fn new() -> Self {
UsersIndex {
list: Vec::new(),
index: HashMap::new(),
}
}
pub fn add(&mut self, user: User) {
self.list.push(user);
self.index.insert(user.id, &user);
}
pub fn get(&self, id: u32) -> Option<&&User> {
self.index.get(&id)
}
}
but can not fix the errors:
use of moved value: user
user does not live long enough
As I understand I have to take ownership of User, but google doesn't help me how to do it.
Rust says that I need to implement the Copy trait, but User contains a field of type String.
The issue with this code is the following:
pub fn add(&mut self, user: User) {
self.list.push(user); // Here, you move user to the list. The list owns user
self.index.insert(user.id, &user); // Then, you create a reference of a moved value
}
So, in your UserIndex struct, you want to store values and references of these values. These are called self-referential structs. With the ownership rules of Rust, you need to use unsafe Rust code to achieve this. If I were you, I'd think of a different way of implementing your collection following the Rust conventions. For example, you could do something like this:
use std::collections::HashMap;
pub struct User {
pub id: u32,
pub name: String,
}
pub struct UsersIndex {
index: HashMap<u32, User>, // I only use the HashMap. The HashMap owns the User structs
}
impl UsersIndex {
pub fn new() -> Self {
UsersIndex {
index: HashMap::new(),
}
}
pub fn add(&mut self, user: User) {
self.index.insert(user.id, user); // user is moved to the HashMap
}
pub fn get(&self, id: u32) -> Option<&User> { // Instead of a nested reference, we get a regular reference
self.index.get(&id)
}
}
fn main() {
let user = User {
id: 42,
name: "test".to_string(),
};
let mut index = UsersIndex::new();
index.add(user);
match index.get(42) {
Some(usr) => println!("{}", usr.name),
_ => println!("Not Found"),
}
}
Here you can find a playground with this implementation.
EDIT
If you need different HashMaps for the same User structs depending on the key used, you can use Rc smart pointers. They allow you to create more than one pointer that owns the same struct. It would look more or less like this:
use std::rc::Rc;
use std::collections::HashMap;
pub struct User {
pub id: u32,
pub name: String,
}
pub struct UsersIndex {
index: HashMap<u32, Rc<User>>, // Keys will be the user indeces
name: HashMap<String, Rc<User>>, // Keys will be the user names
}
impl UsersIndex {
pub fn new() -> Self {
UsersIndex {
index: HashMap::new(),
name: HashMap::new()
}
}
pub fn add(&mut self, user: User) {
// user will be moved, so we copy the keys before that:
let user_id = user.id;
let user_name = user.name.clone();
let user_rc = Rc::new(user); // We create the Rc pointer; user is moved to user_rc
self.index.insert(user_id, user_rc.clone()); // Rc pointers can be cloned
self.name.insert(user_name, user_rc); // user_rc is moved to the self.name HashMap
}
pub fn get(&self, id: u32) -> Option<Rc<User>> {
match self.index.get(&id) {
Some(user) => Some(user.clone()),
None => None
}
}
}
fn main() {
let user = User {
id: 42,
name: "test".to_string(),
};
let mut index = UsersIndex::new();
index.add(user);
match index.get(42) {
Some(usr) => println!("{}", usr.name),
_ => println!("Not Found"),
}
}
Here you can find a playground with this new implementation.
Again, if you also need the User structs to be mutable, then you'll probably need to use an Rc<RefCell<User>> (link here).
Hope you find this useful!
Related
Here is a link to a playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1e82dcd3d4b7d8af89c5c00597d2d938
I am a newbie learning rust and trying to simply update a mutable vector on a struct.
struct Friend<'a> {
name: &'a str
}
impl <'a> Friend<'a> {
fn new(name: &'a str) -> Self { Self { name } }
}
struct FriendsList<'a> {
name: &'a str,
friends: Vec<Friend<'a>>
}
impl <'a> FriendsList<'a> {
fn new(name: &'a str, friends: Vec<Friend<'a>>) -> Self { Self { name, friends } }
fn add_new_friend(&self, friend: Friend) {
// how to make this work?
todo!()
// self.friends.push(friend)
}
}
fn main() {
let friends_list = FriendsList::new("George",
vec![
Friend::new("bob"),
Friend::new("bobby"),
Friend::new("bobbo")
]
);
}
specifically how do I make this fn add_new_friend(&self, friend: Friend) method work? That is, push a new element to field friends on the FriendsList struct. Is there a more idiomatic approach? When I try making things mutable, I get a whole bunch of errors I am not sure how to fix...
You have to borrow self mutably:
impl <'a> FriendsList<'a> {
// [...]
fn add_new_friend(&mut self, friend: Friend<'a>) {
self.friends.push(friend)
}
}
Earlier I had a Sync + Send trait SyncMessenger:
trait Messenger {
fn send_message(&self, user_id: UserId, text: &str);
}
trait SyncMessenger: Messenger + Sync + Send {}
It's implementation:
pub struct DiscordMessenger {
discord: Arc<Discord>, // (Discord is Sync and Send already)
}
impl Messenger for DiscordMessenger {
fn send_message(&self, user_id: UserId, text: &str) {
self.discord.send_message(user_id, text, false);
}
}
impl SyncMessenger for DiscordMessenger {}
And using it:
struct Bot {
messenger: Arc<SyncMessenger>,
}
impl Bot {
pub fn new() -> Bot {
Bot { messenger: Arc::new(DiscordMessenger::new()) }
}
fn messenger(&self) -> Arc<SyncMessenger> {
self.messenger.clone()
}
}
struct PingCommand {
fn fire(&mut self, bot: &mut Bot) {
bot.messenger().send_message(UserId(0), "Pong");
}
}
Everything worked fine. Now I want to implement TestMessenger which does not really send a message through a network but toggles a flag in Self instead:
#[cfg(test)]
struct TestMessenger {
pub message_sent: bool,
}
impl Messenger for TestMessenger {
fn send_message(&mut self, user_id: UserId, text: &str) { // we have `&mut self` here
self.message_sent = true;
}
}
So I need to change send_message(&self) to send_message(&mut self) everywhere (in traits and in implementations). I did that but after I can't compile my user code:
struct PingCommand {
fn fire(&mut self, bot: &mut Bot) {
bot.messenger().send_message(UserId(0), "Pong");
}
}
Gives error:
|
12 | let _ = bot.messenger().send_message(UserId(0),
| ^^^^^^^^^^^^^^^ cannot borrow as mutable
error: aborting due to previous error
I have found something that works but it looks very ugly to me (and requires unwrap() which I would like to avoid):
let _ = Arc::get_mut(&mut bot.messenger()).unwrap().send_message(UserId(0),
So the question here is how to do that as much simple as possible, without unwrap()s, static methods like Arc::get_mut? Why simple fn messenger(&self) -> Arc<SyncMessenger> is not possible to call mut methods?
You can use interior mutability to change data behind immutable references.
use std::cell::Cell;
struct TestMessenger {
pub message_sent: Cell<bool>,
}
impl Messenger for TestMessenger {
fn send_message(&self, user_id: UserId, text: &str) {
self.message_sent.set(true);
}
}
This struct is for single-treaded case. You'll need std::sync::Mutex instead of Cell to have Sync for TestMessenger.
Note that trait methods implemented should be strictly checked for compliance: send_message(&mut self, user_id: UserId, text: &str) is not compliant with send_message(&self, user_id: UserId, text: &str) due to the former's mutable reference to self, and the compiler would eventually complain.
Therefore, interior mutability is required here, so that state changes may happen behind an immutable reference. In this case, since you're dealing with other thread-safe components, you may consider using the thread-safe AtomicBool.
use std::sync::atomic::AtomicBool;
#[cfg(test)]
struct TestMessenger {
pub message_sent: AtomicBool,
}
impl Messenger for TestMessenger {
fn send_message(&self, user_id: UserId, text: &str) { // we have `&mut self` here
self.message_sent.store(true, Ordering::AcqRel);
}
}
I am trying to make an Entity interface for dynamically mapping a database result into a Rust struct:
pub trait Entity {
fn map(&self, Result<QueryResult>) -> Self;
}
pub struct DbQuery<T> {
pub sql: String,
pub params: Vec<Value>,
pub limit: i32,
pub paged: Option<Pagination>,
pub entity: T,
}
pub struct Settings {
pub name: String,
pub value: Option<String>,
}
impl Entity for Settings {
fn map(&self, result: Result<QueryResult>) -> Settings {
// ...
Settings {
name: "hello".to_string(),
value: None,
}
}
}
impl DbMapper {
// ...
pub fn find<T>(&self, query: DbQuery<T>) -> Option<Vec<T>> {
println!("query find SQL: {}", query.sql);
let mut stmt = &self.pool.prepare(query.sql).unwrap();
let ret = Vec::new();
for row in stmt.execute(query.params).unwrap() {
ret.push(query.entity.map(row.unwrap()));
}
Some(ret)
}
}
But I get an error:
error: no method named map found for type T in the current scope
ret.push(query.entity.map(row.unwrap())); |
note: the method map exists but the following trait
bounds were not satisfied: T : std::iter::Iterator = help: items
from traits can only be used if the trait is implemented and in scope;
the following traits define an item map, perhaps you need to
implement one of them: = help: candidate #1:
models::holders::database::Entity = help: candidate #2:
std::iter::Iterator
Here is a version of your code that runs on the playground and replicates your issue:
pub struct QueryResult;
pub struct Value;
pub struct Pagination;
pub struct DbMapper;
pub trait Entity {
fn map(&self, Result<QueryResult, ()>) -> Self;
}
pub struct DbQuery<T> {
pub sql: String,
pub params: Vec<Value>,
pub limit: i32,
pub paged: Option<Pagination>,
pub entity: T,
}
pub struct Settings {
pub name: String,
pub value: Option<String>,
}
impl Entity for Settings {
fn map(&self, result: Result<QueryResult, ()>) -> Settings {
// ...
Settings {
name: "hello".to_string(),
value: None,
}
}
}
impl DbMapper {
// ...
pub fn find<T>(&self, query: DbQuery<T>) -> Option<Vec<T>> {
println!("query find SQL: {}", query.sql);
// ########## attempt to call map()
let _ = query.entity.map(Ok(QueryResult {}));
let ret = Vec::new();
Some(ret)
}
}
fn main() {}
The problem is that T in the DbQuery<T> argument in the find method has no idea that T is an Entity type. So we need to tell it:
pub fn find<T>(&self, query: DbQuery<T>) -> Option<Vec<T>>
where T: Entity
{
// ... code here ...
}
This now compiles and runs.
The compiler now knows that T is an Entity of some description, and it can call the map method on it.
I'm having problems with a struct containing a hashmap in rust.
Lets say I have the following types:
pub type KeyType i32;
pub enum StatusType { Locked, Unlocked }
pub struct Entry {
key: KeyType,
status: StatusType
}
pub struct Manager<'a> {
map: HashMap<KeyType, &'a mut Entry>
}
I want to define a method on Manager that takes a key and returns either a unlocked entry if not found, or the existing entry if it exists. Here is the pseudo code:
impl<'a> Manager<'a> {
pub fn find_or_create_entry(&'a mut self, key: KeyType) -> &'a mut Entry {
match self.map.get(&key) {
Some(e) => e,
None => {
// create new entry, add to map, and return mutable ref
}
}
}
}
I haven't been able to figure out how this works in Rust. Any pointers?
I got around this by changing the type of the HashMap to HashMap<KeyType,Box<Entry>>, and implemented the method as follows:
pub fn get_or_create_entry(& mut self, key: LockKey) -> &mut LockEntry {
let e = Box::new(LockEntry{key: key, status: LockStatus::Unlocked});
self.lock_table.entry(key).or_insert(e)
}
Is there a better way?
I need help understanding lifetime specifiers. I think I get the concept of lifetimes. I watched Memory, Ownership and Lifetimes. I just think if I could work through this small example it might help me with the syntax of lifetimes. A topic I, to date, find confusing.
use std::collections::HashMap;
fn main() {
pub struct User<'a> {
pub name: & 'a str
}
impl <'a>User<'a> {
pub fn new(uname: & 'a str, pwd: & 'a str) -> User {
User{name: uname}
}
}
pub struct ChatRoom<'a> {
pub name: & 'a str,
pub users: HashMap<& 'a str, User>
}
impl <'a>ChatRoom<'a> {
pub fn new(name: &str) -> ChatRoom {
let users = HashMap::new();
ChatRoom {name: name, users: users}
}
pub fn join(&mut self, user: User) {
self.users.insert(user.name, user);
}
}
let mut room = ChatRoom::new("Test");
let user = User::new("bender","123");
room.join(user);
}
I'm not sure what your exact question is, so I imagine you wanted that code to compile. Check this playground.
Notice that lifetime parameters are part of the type, so you want User<'a> not just User.
use std::collections::HashMap;
fn main() {
struct User<'a> {
name: &'a str,
}
impl<'a> User<'a> {
fn new(uname: &'a str, pwd: &'a str) -> User<'a> {
User { name: uname }
}
}
struct ChatRoom<'a> {
name: &'a str,
users: HashMap<&'a str, User<'a>>,
}
impl<'a> ChatRoom<'a> {
fn new(name: &str) -> ChatRoom {
let users = HashMap::new();
ChatRoom {
name: name,
users: users,
}
}
fn join(&mut self, user: User<'a>) {
self.users.insert(user.name, user);
}
}
let mut room = ChatRoom::new("Test");
let user = User::new("bender", "123");
room.join(user);
}