How to design a Map which can be sorted based on one of the values? - reflection

Why this question exists?
I am writing rabbithole-rs, which is a JSON:API implementation based on Rust.
But while designing the sorting/pagination features, I meet a big problem on on designing the attribute fields.
Intro JSON:API soring/pagination briefly
There is a attributes fields in JSON:API's resource object, which is a HashMap<String, serde_json::Value> or HashMap<String, Box<Any>>,
where the key is attribute name, and the value is the attribute's value.
And Vec<Resource> object has a sort(attribute_name: &str) method, which can sort a bunch of Resource based on one of the attribute.
A brief layout of Resource
pub struct Resource {
pub ty: String, // The type of `Resource`, `sort` method can only effect on the same type of resources
pub id: String, // Every `Resource` has an unique id
pub attr1: dyn Ord + Serialize + Deserialize,
... <more attrs>
}
Where:
Each attr should implement at least three traits:
Ord to compare
Serialize to convert into serde_json::Value
Deserialize to convert from serde_json::Value
What the problem is?
When getting a Resource, I have to extract all of the attribute fields and put them into a HashMap, like:
HashMap<String, serde_json::Value> or HashMap<String, Box<Any>>.
But both of them lose the trait info of the attributes, so I cannot compare two attribute item with the same name.
A brief demo, please!
Sure! Here you go!
#[macro_use]
extern crate serde_derive;
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::collections::HashMap;
/// This is a struct field
#[derive(Debug, Serialize, Deserialize, Ord, PartialOrd, PartialEq, Eq)]
pub struct Name {
pub first_name: String,
pub last_name: String,
}
/// Resource object
#[derive(Debug, Serialize, Deserialize, Ord, PartialOrd, PartialEq, Eq)]
pub struct Resource {
pub ty: String,
pub id: String,
pub age: i32, // attr 1
pub name: Name, // another attr
}
fn main() {
let item1 = Resource {
ty: "human".into(),
id: "id1".to_string(),
age: 1,
name: Name {
first_name: "first1".to_string(),
last_name: "last1".to_string(),
},
};
// **This is the first attributes HashMap**
let mut map_item1: HashMap<&str, (&str, Box<dyn Any>)> = Default::default();
map_item1.insert("age", ("i32", Box::new(item1.age)));
map_item1.insert("name", ("Name", Box::new(item1.name)));
let item2 = Resource {
ty: "human".into(),
id: "id2".to_string(),
age: 2,
name: Name {
first_name: "first2".to_string(),
last_name: "last2".to_string(),
},
};
// **This is the second attributes HashMap**
let mut map_item2: HashMap<&str, (&str, Box<dyn Any>)> = Default::default();
map_item2.insert("age", ("i32", Box::new(item2.age)));
map_item2.insert("name", ("Name", Box::new(item2.name)));
// TODO: NEED TO BE IMPLEMENTED
for k in map_item1.keys() {
println!(
"key: {key}, item1.{key} < item2.{key}? {res}",
key = k,
res = map_item1.get(k).unwrap().1.cmp(map_item2.get(k).unwrap().1)
)
}
}

Related

How to work w/ mutable vector attached to a struct instance in rust

I'm new in rust. I'm trying to create a json parser and I need a struct instance to hold a mutable vector that can be populated with more and more items.
Here is my code:
#[derive(Debug, PartialEq)]
pub enum Token {
BegKey,
EndKey
}
#[derive(Debug)]
struct TokenData {
token: Token,
data: Vec<u8>
}
impl TokenData {
fn new(token: Token, v: Option<u8>) -> Self {
let mut data: Vec<u8> = vec![];
for &val in v.iter() {
data.push(val);
}
TokenData{token: token, data: data }
}
}
pub fn read_json_stream(buf: &[u8], size: usize, tokens: &mut Vec<TokenData>, on_obj: fn() -> ()) {
let mut i = 0;
while i < size {
let token = tokens.last();
match token {
// ^^^^^^^
// cannot move out of `token.0.data` which is behind a shared reference
// note: move occurs because these variables have types that don't implement the `Copy` traitrustc(E0507)
Some(&TokenData{token: Token::BegKey, data: mut data}) => match buf[i] {
b'"' if data.last() == Some(&b'\\') => data.push(b'"'),
b'"' => tokens.push(TokenData::new(Token::EndKey, None)),
k => data.push(k),
},
_ => {}
}
i += 1;
}
}
You need to use .last_mut() to get a mutable reference. Then remove & and mut data from the match arm to let match ergonomics take care of the rest:
let token = tokens.last_mut();
match token {
Some(TokenData { token: Token::BegKey, data }) => ...
Playground
What you want is the last item, but owned Option<T>, for that use pop instead of last (which returns an Option<&T>):
let token = tokens.pop();
Playground

How to convert Vec<Item> to Vec<String>?

I have the following object, and trying to convert Vec<Courses> and retrieve CourseName.
pub struct Schools {
pub courses: Vec<CourseName>,
}
pub struct CourseName(String);
impl CourseName {
pub fn as_str(&self) -> &str {
&self.0[..]
}
}
Trying to get the Vec<String>, but my following approach does not work,
assigned_courses:Vec<String> = courses.iter().map(|c| c.clone().as_str()).collect()
getting the following error:
value of type `Vec<std::string::String>` cannot be built from `std::iter::Iterator<Item=&str>`
Update:
The map closure receives a &CourseName so clone just copies the reference. What you instead want is to access the tuple and clone the inner String with c.0.
let assigned_courses: Vec<String> = courses.iter().map(|c| c.0.clone()).collect();
Alternatively, if references to the course names are enough, then instead you can use as_str on the inner String.
let assigned_courses: Vec<&str> = schools.courses.iter().map(|c| c.0.as_str()).collect();
To fix the "private field" error. You can add a visibility modifier, e.g.
pub struct CourseName(pub String);
However, it's probably better to keep it as private, and instead add a method like as_str().
impl CourseName {
pub fn as_str(&self) -> &str {
&self.0
}
}
Then resulting in:
let assigned_courses: Vec<String> = schools.courses.iter().map(|c| c.as_str().to_string()).collect();
Alternatively, you could also impl AsRef<str> and/or Display for CourseName, to make everything more generalized.
Assuming that CourseName is just to have a typed version instead of a String. Then you could instead impl Display for CourseName.
use std::fmt;
pub struct CourseName(String);
impl fmt::Display for CourseName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
This allows you to do println!("{}, course") along with course.to_string().
let assigned_courses: Vec<String> = schools.courses.iter().map(|c| c.to_string()).collect();
Working example:
#[derive(Debug)]
pub struct CourseName(pub String);
fn courses_to_strings(list: &[CourseName]) -> Vec<String> {
list.iter().map(|course| course.0.clone()).collect()
}
fn main() {
let courses: Vec<CourseName> = vec![
CourseName("a".to_string()),
CourseName("b".to_string())
];
let strings = courses_to_strings(&courses);
dbg!(strings);
}
playground
All you needed to do was clone the String instead of the CourseName tuple struct in your map function, and also add the pub visibility modifier to the internal String.

Recursive structs error lifetime (cannot infer an appropriate lifetime for lifetime parameter in function call... [E0495]) [duplicate]

This question already has an answer here:
What lifetimes do I use to create Rust structs that reference each other cyclically?
(1 answer)
Closed 6 years ago.
I can't figure out the lifetime parameters for this code. Everything I try usually results in a compiler error:
consider using an explicit lifetime parameter as shown
or something like
in type &'ent Entity<'a, 'ent>, reference has a longer lifetime than the data it references.
Entity, Reference are simplified versions to keep this example minimal.
struct Entity<'a> {
id: i32,
name: &'a str,
references: Option<Vec<Reference<'a>>>,
}
struct Reference<'a> {
entity: &'a Entity<'a>,
}
fn main() {
let mut ents: Vec<Entity> = vec![Entity {
id: 0,
name: "Zero",
references: None,
},
Entity {
id: 1,
name: "One",
references: None,
},
Entity {
id: 2,
name: "Two",
references: None,
},
Entity {
id: 3,
name: "Three",
references: None,
}];
let references_ents_id = vec![vec![3, 1, 2], vec![1], vec![0, 3], vec![3, 0]];
create_references(&references_ents_id, &mut ents);
}
fn create_references(refs_id: &Vec<Vec<i32>>, ents_vec: &mut Vec<Entity>) {
for (id_ent, references) in refs_id.iter().enumerate() {
let mut references_of_ent: Vec<Reference> = vec![];
for id_ent in references {
references_of_ent.push(Reference {
entity: ents_vec.iter().find(|ent| ent.id == *id_ent).unwrap(),
});
}
ents_vec[id_ent].references = Some(references_of_ent);
}
}
Rust Playground
I was looking in the wrong direction. And so, I found a solution, but unfortunately it is not safe.
You can implement it using Rc and Weak to allow shared ownership of nodes, although this approach pays the cost of memory management.
You can implement it using unsafe code using raw pointers. This will be more efficient, but bypasses Rust’s safety guarantees.
Using borrowed references with UnsafeCell.
Rust FAQ
Other answer on SO
Example of implementation unsafe version with raw pointers:
struct Entity<'a> {
id: i32,
name: &'a str,
references: Option<Vec<Reference<'a>>>,
}
struct Reference<'a> {
entity: *const Entity<'a>,
}
Rust Playground: https://play.rust-lang.org/?gist=8237d8cb80a681c981a85610104f2e5c&version=stable&backtrace=0

How to get struct field names in Rust? [duplicate]

This question already has answers here:
Is there a way to get the field names of a struct in a macro?
(3 answers)
Closed 6 years ago.
Is there some equivalent of JS's Object.keys() for Rust's struct?
I need something to generate CSV headers (I use rust-csv) from structure field names.
struct Export {
first_name: String,
last_name: String,
gender: String,
date_of_birth: String,
address: String
}
//... some code
let mut wrtr = Writer::from_file("/home/me/export.csv").unwrap().delimiter(b'\t');
wrtr.encode(/* WHAT TO WRITE HERE TO GET STRUCT NAMES as tuple of strings or somethings */).is_ok()
The current main method of metaprogramming in Rust is via macros. In this case, you can capture all the field names and then add a method that returns string forms of them:
macro_rules! zoom_and_enhance {
(struct $name:ident { $($fname:ident : $ftype:ty),* }) => {
struct $name {
$($fname : $ftype),*
}
impl $name {
fn field_names() -> &'static [&'static str] {
static NAMES: &'static [&'static str] = &[$(stringify!($fname)),*];
NAMES
}
}
}
}
zoom_and_enhance!{
struct Export {
first_name: String,
last_name: String,
gender: String,
date_of_birth: String,
address: String
}
}
fn main() {
println!("{:?}", Export::field_names());
}
For advanced macros, be sure to check out The Little Book of Rust Macros.

Trait is not implemented for the type `&A` when passing an array of pairs to a function

I am trying to write the function set which calls the Rust LMDB library (docs), and an example I'm working off of.
I can't for the life of me get this to work. Here is my current attempt:
fn main() {
let env = getenv("duperdb");
let dbhandle = get_dbhandle("", &env);
let txn = new_transaction(&env);
let vec = vec![("foo", "another text"), ("bar", "and another")];
set(&dbhandle, &env, &vec);
let reader = env.get_reader().unwrap();
let db = reader.bind(&dbhandle);
let note = db.get::<&str>("foo").unwrap();
println!("NOTE: {}", note);
}
Where set is defined as:
pub fn set<A: ToMdbValue, B: ToMdbValue>(
handle: &DbHandle,
env: &Environment,
pairs: &Vec<(&A, &B)>) -> () {
let txn = new_transaction(&env);
{
let db = txn.bind(&handle);
for &(id, note) in pairs.iter() {
db.set(&id, &note).unwrap();
}
}
match txn.commit() {
Err(_) => panic!("Failed to commit!"),
Ok(_) => (),
}
}
This spits out the following error:
src/db/wrapper.rs:28:20: 28:23 error: the trait `lmdb::traits::ToMdbValue` is not implemented for the type `&A` [E0277]
src/db/wrapper.rs:28 db.set(&id, &note).unwrap();
^~~
I also tried db.set(id, note).unwrap();, but this time I get:
src/main.rs:13:5: 13:8 error: the trait `core::marker::Sized` is not implemented for the type `str` [E0277]
src/main.rs:13 set(&dbhandle, &env, &vec);
^~~
src/main.rs:13:5: 13:8 help: run `rustc --explain E0277` to see a detailed explanation
src/main.rs:13:5: 13:8 note: `str` does not have a constant size known at compile-time
src/main.rs:13:5: 13:8 note: required by `dupernote::db::wrapper::set`
src/main.rs:13:5: 13:8 error: the trait `lmdb_rs::traits::ToMdbValue` is not implemented for the type `str` [E0277]
src/main.rs:13 set(&dbhandle, &env, &vec);
^~~
I also tried stuff like:
for (id, note) in pairs.iter() {
db.set(id, note).unwrap();
}
But that doesn't work either... I don't fully understand why. Doesn't id and note have type &str, not str?
Here's an MCVE of your problem:
trait Example {}
impl Example for i32 {}
fn library_call<T>(value: T)
where T: Example,
{}
fn user_call<T>(values: &[T])
where T: Example,
{
for i in values {
library_call(i);
}
}
fn main() {
let values = vec![1, 2, 3];
user_call(&values);
}
With the error:
error: the trait `Example` is not implemented for the type `&T` [E0277]
library_call(i);
^~~~~~~~~~~~
The error message is exactly correct - Example is not implemented for &T, it's only guaranteed to be implemented for T. &T and T are different types.
Instead, you need to indicate that a reference to the generic type implements the trait you need:
fn user_call<T>(values: &[T])
where for <'a> &'a T: Example,
And then you need to make sure that a reference to the concrete type actually implements the trait:
impl<'a> Example for &'a i32 {}
Or a broader version:
impl<'a, T> Example for &'a T
where T: Example
{}
See also When should I not implement a trait for references to implementors of that trait?
The definition of the function that gives you an error (if I'm reading the docs right):
fn set(&self, key: &ToMdbValue, value: &ToMdbValue) -> MdbResult<()>
key must be a reference to a trait object. You are trying to pass a reference to a generic type implmementing ToMdbValue.
https://doc.rust-lang.org/book/trait-objects.html
I can't verify but this should work:
pub fn set(handle: &DbHandle, env: &Environment, pairs: &Vec<(&ToMdbValue, &ToMdbValue)>) -> () {
let txn = new_transaction(&env);
{
let db = txn.bind(&handle);
for &(id, note) in pairs.iter() {
db.set(id, note).unwrap();
}
}
match txn.commit() {
Err(_) => panic!("Failed to commit!"),
Ok(_) => (),
}
}
Other things: you may want to work with boxed trait objects Box<ToMdbValue>. The link above explains it. You should pass a &[YourType] rather than &Vec<[YourType]>.
I managed to get it working. I'm not sure how kosher this solution is, but I'll post it.
So now, in main(), I do the following (example with an (int, string) kv pair):
let k = 1;
let val = "hello there";
let vec = vec![(&k, &val)];
set(&dbhandle, &env, &vec);
I had to declare them separately since vec![(&1, &"hello there")] threw an error of the form borrowed value does not live long enough.
set now looks like this:
pub fn set<A, B>(handle: &DbHandle, env: &Environment, pairs: &Vec<(&A, &B)>)
-> ()
where A: ToMdbValue,
B: ToMdbValue {
let txn = new_transaction(&env);
{
let db = txn.bind(&handle);
for &(id, note) in pairs.iter() {
db.set(id, note).unwrap();
}
}
match txn.commit() {
Err(_) => panic!("Failed to commit!"),
Ok(_) => (),
}
}

Resources