We have a static list of objects. We would like to create a static constant HashMap with the key being the names of the objects.
How do we do that?
As an example, here is the definition of a Book object:
struct Book<'a> {
name: &'a str,
author: &'a str,
id: i32,
}
Here is a static list of 3 books:
const ALL_BOOKS: &'static [Book] = &[
Book {
name: "Zero to One",
author: "James",
id: 2,
},
Book {
name: "Zero to One",
author: "Tom",
id: 1,
},
Book {
name: "Turtle all the way",
author: "Jerry",
id: 1,
},
];
We want to defined a dictionary of books with the key being the name of the book, and the value being a list of book with that name.
I notice that we have lazy_static from:
Rust best practices when specifying a constant hash map
Global mutable HashMap in a library
Here is what I tried:
lazy_static! {
static ref ALL_BOOKS_BY_SHORT_NAME: HashMap<String, Vec<Book<'static>>> = {
let mut map = HashMap::new();
for book in ALL_BOOKS {
if !map.contains_key(book.name) {
map.insert(book.name.to_owned(), vec![book]);
} else {
let mut list = map.get(book.name).unwrap();
list.push(book)
}
}
map
};
}
Here is the error:
error[E0308]: mismatched types
--> src/main.rs:42:9
|
30 | static ref ALL_BOOKS_BY_SHORT_NAME: HashMap<String, Vec<Book<'static>>> = {
| ----------------------------------- expected `HashMap<String, Vec<Book<'static>>>` because of return type
...
42 | map
| ^^^ expected struct `Book`, found `&Book<'_>`
|
= note: expected struct `HashMap<_, Vec<Book<'static>>>`
found struct `HashMap<_, Vec<&Book<'_>>>`
What does the '_ mean here? How do we solve that?
The '_ is "some lifetime". The actual lifetime is 'static, but the compiler doesn't know that at this stage of the compilation.
The problem is that you're iterating over a slice of Book<'static>s. And inserting items to the map. But iteration over a slice produces references, while your maps is expecting owned Books.
The fix is simple: clone or copy the books.
#[derive(Clone, Copy)]
struct Book<'a> { ... }
lazy_static! {
static ref ALL_BOOKS_BY_SHORT_NAME: HashMap<String, Vec<Book<'static>>> = {
let mut map = HashMap::new();
for book in ALL_BOOKS {
if !map.contains_key(book.name) {
map.insert(book.name.to_owned(), vec![*book]);
} else {
let mut list = map.get(book.name).unwrap();
list.push(*book)
}
}
map
};
}
Some more notes:
Prefer once_cell to lazy_static (its API is going to be part of std).
Use the entry API:
use once_cell::sync::Lazy;
static ALL_BOOKS_BY_SHORT_NAME: Lazy<HashMap<String, Vec<Book<'static>>>> = Lazy::new(|| {
let mut map = HashMap::<_, Vec<_>>::new();
for book in ALL_BOOKS {
map.entry(book.name.to_owned()).or_default().push(*book);
}
map
});
This allocates a string even for duplicate keys but given that this is only an initialization routine this likely doesn't matter.
Related
I would like to filter into an iterable that contain a struct of Person with map() instruction but I don't know if it's possible on this way?
I would like to know which is the bast way to complete this task properly in Rust.
// This my structure of objet Person
#[derive(Debug, Clone)]
struct Person {
product_id: i64,
nom: String,
prenom: String,
email: Option<String>,
actif: bool,
}
// Main function which create a person and add this person into a HashMap<i64, Person>
fn main() {
// create list of person
let mut personnes = Personnes::new();
// Create one person
let person = Person {
product_id: 1,
nom: String::from("TestNom"),
prenom: String::from("TestPrenom"),
email: Some("test#mail.com".to_string()),
actif: true,
};
// Add person into my iterable
personnes.add(person);
// Add few persons...
// Imagine multiple Person and find person who actif = true
persons_actives(&personnes);
}
// Something like that :
fn persons_actives(personnes: &Personnes) {
let persons_actives = personnes
.inner
.iter()
.map(|person.actif| person.actif == true)
.collect();
}
// But it's maybe impossible in this way ?
I tried :
fn persons_actives(personnes: &Personnes) {
let persons_actives = personnes.inner.iter().filter(|person| person.actif == true).collect();
}
and :
fn persons_actives(personnes: &Personnes) {
let persons_actives = personnes.inner.iter().find(|person| person.actif == true).collect();
}
but i have the same error :
"no field actif on type &(&i64, &Person)"
You can iterate through your HashMap values, then filter() and collect() the result into a Vector.
use std::collections::HashMap;
#[derive(Debug)]
struct Person {
is_active: bool
//other params
}
fn main() {
// define your map
let mut map :HashMap<i32,Person> = HashMap::new();
// init a Person
let person: Person = Person{
is_active: true
// set other params
};
// insert it to map
map.insert(1,person);
// filter person.is_active == true
let active_persons : Vec<_> = map.values().filter(|person| person.is_active==true).collect();
// result
println!("{:?}",active_persons);
}
Now you have your desired result in active_persons.
Thanks for your reply,
I have tested this one :
let active_persons : Vec<_> = personnes.inner.values().filter(|&person| if_else!(person.actif, true, false)).collect();
with ternary-rs library for the if_else syntax.
Your right i shold have noticed the structiure of Personnes which is :
struct Personnes {
inner: HashMap<i64, Person>,
}
But i have keep your Vec<_> Structure for the active person.
I created two persons, one is active and the other not and it's worked !
Thanks a lot !
I am trying to create a vector of trait objects but I'm getting a type mismatch. I also tried using .map() instead of a for loop but had the same problem. This is a minimal version of my real code, I realise this example doesn't require the use of trait objects but my real code does since there will be types other than Link in the List, which also implement Speak. How can I create a Vec with the correct type so I can pass it to trait_objects?
use std::fs;
fn main() {
// let links = fs::read_dir("my_dir").unwrap();
// let links: Vec<Box<dyn Speak>> = links
// .map(|file| {
// let myfile = file.unwrap();
// let href = myfile.path().to_str().unwrap();
// Box::new(Link { attr: href })
// })
// .collect();
let links = Vec::new();
for entry in fs::read_dir("my_dir").unwrap() {
let myfile = entry.unwrap();
let href = myfile.path().to_str().unwrap();
links.push(Box::new(Link { attr: href }));
}
let link_objects = ListOfTraitObjects {
trait_objects: links,
};
for link in link_objects.trait_objects.iter() {
println!("{}", link.speak());
}
}
trait Speak {
fn speak(&self) -> String;
}
struct ListOfTraitObjects {
trait_objects: Vec<Box<dyn Speak>>,
}
struct Link<'a> {
attr: &'a str,
}
impl Speak for Link<'_> {
fn speak(&self) -> String {
self.attr.to_string()
}
}
There are several issues with your code. The first is that you're creating a Vec<Box<Link>> instead of a Vec<Box<Speak>>. By default Box::new (foo) creates a box of the same type as foo. If you want a box of some trait implemented by foo, you need to be explicit:
links.push(Box::new(Link { attr: href }) as Box::<dyn Speak>);
Playground
However this gives two new errors:
error[E0716]: temporary value dropped while borrowed
--> src/lib.rs:7:20
|
7 | let href = myfile.path().to_str().unwrap();
| ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
8 | links.push(Box::new(Link { attr: href }) as Box::<dyn Speak>);
| ----------------------------- cast requires that borrow lasts for `'static`
error[E0596]: cannot borrow `links` as mutable, as it is not declared as mutable
--> src/lib.rs:8:9
|
4 | let links = Vec::new();
| ----- help: consider changing this to be mutable: `mut links`
...
8 | links.push(Box::new(Link { attr: href }) as Box::<dyn Speak>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
The first error is because your Link struct borrows a string from entry, but entry only lives as long as the current loop iteration. Even though the error states that the cast requires a 'static lifetime, you would get a similar error if you were simply trying to build a Vec<Link> without casting (playground). You need to change the definition of your Link struct to contain an owned string:
struct Link {
attr: String,
}
impl Speak for Link {
fn speak(&self) -> String {
self.attr.clone()
}
}
The second error is easier and can be fixed simply by following the compiler suggestion and replacing let links = … with let mut links = ….
Full working example:
use std::fs;
fn main() {
let mut links = Vec::new();
for entry in fs::read_dir("my_dir").unwrap() {
let myfile = entry.unwrap();
let href = myfile.path().to_str().unwrap().to_string();
links.push(Box::new(Link { attr: href }) as Box::<dyn Speak>);
}
let link_objects = ListOfTraitObjects {
trait_objects: links,
};
for link in link_objects.trait_objects.iter() {
println!("{}", link.speak());
}
}
trait Speak {
fn speak(&self) -> String;
}
struct ListOfTraitObjects {
trait_objects: Vec<Box<dyn Speak>>,
}
struct Link {
attr: String,
}
impl Speak for Link {
fn speak(&self) -> String {
self.attr.clone()
}
}
Playground
The same fixes can be applied to your iterator-based solution:
let links = fs::read_dir("my_dir").unwrap();
let links: Vec<_> = links
.map(|file| {
let myfile = file.unwrap();
let href = myfile.path().to_str().unwrap().to_string();
Box::new(Link { attr: href }) as Box<dyn Speak>
})
.collect();
Playground
I am not able to print values from a Vec from a module, could you please help?
Here is the content of the function in the module:
pub struct Author {
_id: i32,
name: String,
country: String
}
pub fn get_saved_records() -> Result<Vec<Author>,Error>{
let mut client = Client::connect("postgresql://***:***#localhost/****",NoTls)?;
let records = client.query("SELECT * FROM author",&[])?;
let mut the_records : Vec<Author> = vec![];
for record in records{
let author = Author {
_id: record.get(0),
name: record.get(1),
country: record.get(2),
};
the_records.push(author);
}
Ok(the_records)
}
Here is the main file where I call get_saved_records() function:
match db::get_saved_records(&mut conn){
Ok(records) => {
for record in records{
println!("{}",record.name);
}
},
Err(_) => println!("No record found")
}
I am getting the error message: field name of struct db::Author is private.
Thank you.
The members of your struct need to be declared pub in order to be directly accessible.
Here, only the name member is accessible.
pub struct Author {
_id: i32,
pub name: String,
country: String
}
Note that it makes sense for a struct to be public while having its
members private.
This ensures encapsulation: the members of the struct are only accessible
from the methods of its implementation (and the surrounding code in the module).
Of course, those methods can also be made public or private.
How can I perform an equality comparison on substrate Hash trait?
Say I have the following code, with owned_vec contains a vector of Hash:
use support::{decl_module, decl_storage, decl_event, dispatch::Result,
StorageValue, StorageMap, ensure, traits::Currency };
use system::ensure_signed;
// this is needed when you want to use Vec and Box
use rstd::prelude::*;
use runtime_primitives::traits::{ As, Hash };
use parity_codec::{ Encode, Decode };
// Our own Cat struct
#[derive(Encode, Decode, Default, Clone, PartialEq, Debug)]
pub struct Kitty<Hash, Balance> {
id: Hash,
name: Option<Vec<u8>>,
base_price: Balance, // when 0, it is not for sale
}
// This module's storage items.
decl_storage! {
trait Store for Module<T: Trait> as CatAuction {
Kitties get(kitties): map T::Hash => Kitty<T::Hash, T::Balance>;
KittyOwner get(owner_of): map T::Hash => Option<T::AccountId>;
OwnedKitties get(kitties_owned): map T::AccountId => Vec<T::Hash> = Vec::new();
}
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn deposit_event<T>() = default;
pub fn transaction(origin, kitty_id: T::Hash) -> Result {
let sender = ensure_signed(origin)?;
let kitty_owner = Self::owner_of(kitty_id).ok_or("Kitty has no owner.")?;
let mut kitty = Self::kitties(kitty_id);
<OwnedKitties<T>>::mutate(kitty_owner, |owned_vec| {
let kitty_index = 0;
for (i, el) in owned_vec.iter().enumerate() {
// This is where the compilation error occurs!
if el != kitty.id { continue }
kitty_index = i;
}
owned_vec.remove(kitty_index);
});
}
}
}
It gives me compiler error that:
no implementation for `&<T as srml_system::Trait>::Hash == <T as srml_system::Trait>::Hash
help: the trait `core::cmp::PartialEq<<T as srml_system::Trait>::Hash>` is not implemented for `&<T as srml_system::Trait>::Hash`
help: consider adding a `where &<T as srml_system::Trait>::Hash: core::cmp::PartialEq<<T as srml_system::Trait>::Hash>` bound
Thank you!
p.s: Aware the tutorial says that looping through a vector is not encouraged in runtime module implementation.
This question already has an answer here:
"cannot move out of borrowed context" and "use of moved value"
(1 answer)
Closed 7 years ago.
I have this code:
enum MyEnum1 {
val1,
val2
}
struct Struct1 {
field1: MyEnum1,
field2: String
}
fn fn1(a: Struct1, b: String, c: String) {
let let1 = fn2(&a.field1);
}
fn fn2(a: &MyEnum1) {
let a11 = *a; // error
let s = Struct1 { field1: a11, field2: "fdsfds".to_string() };
}
fn main() {
println!("Hello World!");
}
The error is error: cannot move out of borrowed content
I was suggested by the compiler to use ref or ref mut, I tried to use them and still that didn't help.
fn fn2(a: &MyEnum1) {
let ref a11 = *a; // still the error
let s = Struct1 { field1: *a11, field2: "fdsfds".to_string() };
}
Short answer
The function fn2 receives a reference to a MyEnum1 as a parameter, but the Struct1 contains an owned MyEnum1. This means that you are actually trying to turn a reference into an owned value, which is only possible if you copy the data.
Long answer
Your code would work if MyEnum1 implemented the Copy trait (which in turn requires implementing the Clone trait). If a type implements Copy, it will be automatically copied when you dereference it and when you assign it (for example, the i32 type in the standard library implements Copy). Try something like the following:
#[derive(Clone, Copy)]
enum MyEnum1 {
val1,
val2
}
You may also choose to implement only Clone and clone the object explicitly, in case your data structure is expensive to copy (for example, the String struct in the standard library implements Clone but not Copy). Then you would need to use the following code:
#[derive(Clone)]
enum MyEnum1 {
val1,
val2
}
fn fn2(a: &MyEnum1) {
let s = Struct1 { field1: a.clone(), field2: "fdsfds".to_string() };
}
Finally, you may also choose to pass the MyEnum1 by value, instead of by reference. Then, the only change you need to apply is:
fn fn1(a: Struct1, b: String, c: String) {
let let1 = fn2(a.field1);
}
fn fn2(a: MyEnum1) {
let s = Struct1 { field1: a, field2: "fdsfds".to_string() };
}