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.
Related
This question already has answers here:
How to use async/await in Rust when you can't make main function async
(4 answers)
How do I synchronously return a value calculated in an asynchronous Future?
(3 answers)
Closed 3 months ago.
I have a program that does various simple things based on user selection.
fn main() {
let mut user_input = String::new(); // Initialize variable to store user input
println!("Select an option:\r\n[1] Get sysinfo\r\n[2] Read/Write File\r\n[3] Download file\r\n[4] Exit"); // Print options
io::stdin().read_line(&mut user_input).expect("You entered something weird you donkey!"); // Get user input and store in variable
let int_input = user_input.trim().parse::<i32>().unwrap(); // Convert user input to int (i32 means signed integer 32 bits)
match int_input { // If Else statement
1 => getsysinfo(), // If int_input == 1, call getsysinfo()
2 => readwritefile(),
3 => downloadfile(),
4 => process::exit(1), // end program
_ => println!("You didn't choose one of the given options, you donkey!") // input validation
}
}
My function downloadfile() looks like this, referenced from the Rust cookbook on downloading files.
error_chain! {
foreign_links {
Io(std::io::Error);
HttpRequest(reqwest::Error);
}
}
async fn downloadfile() -> Result<()> {
let tmp_dir = Builder::new().prefix("example").tempdir()?;
let target = "localhost:8000/downloaded.txt";
let response = reqwest::get(target).await?;
let mut dest = {
let fname = response
.url()
.path_segments()
.and_then(|segments| segments.last())
.and_then(|name| if name.is_empty() { None } else { Some(name) })
.unwrap_or("tmp.bin");
println!("File to download: {}", fname);
let fname = tmp_dir.path().join(fname);
println!("Will be located under: {:?}", fname);
File::create(fname)?
};
let content = response.text().await?;
copy(&mut content.as_bytes(), &mut dest)?;
Ok(())
}
I get the following error:
`match` arms have incompatible types
expected unit type `()`
found opaque type `impl Future<Output = std::result::Result<(), Error>>`
I presume its because the async function returns a Future type, so how can I make this code work?
You need to use the block_on function.
Add futures as a dependency in your cargo.toml for the following example to work.
use futures::executor::block_on;
async fn hello() -> String {
return String::from("Hello world!");
}
fn main() {
let output = block_on(hello());
println!("{output}");
}
I have a scenario where I have to convert the entire struct into a map.
I know we have a library structs.Map(s) which will convert the struct to a map. But I want to know is there a way where i can convert multiple struct inside struct to map[string]interface.
for example we have below
package main
import (
"log"
"github.com/fatih/structs"
)
type Community struct {
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Sources []Source `json:"sources,omitempty"`
Moderators []string `json:"moderators,omitempty"`
}
type Source struct {
SourceName string `json:"sourceName,omitempty"`
Region []State `json:"region,omitempty"`
}
type State struct {
State1 string `json:"state1,omitempty"`
State2 string `json:"state2,omitempty"`
}
func main() {
compareData := Community{
Name: "A",
Description: "this belong to A community",
Sources: []Source{
{
SourceName: "SourceA",
Region: []State{
{
State1: "State1",
},
{
State2: "State2",
},
},
},
},
}
m := structs.Map(compareData)
log.Println(m)
}
this will give result as below ,that is it is creating map for the inside struct again
map[Description:this belong to A community
Moderators:[]
Name:A Sources:[map[SourceName:SourceA Region:[map[State1:State1 State2:] map[State1: State2:State2]]]]]
my expectation is get only a single map[string]interface{}
map[
Description:this belong to A community
Moderators:[]
Name:A
SourceName:SourceA
State1:State1
State2:State2
]
my purpose of creating a single map is to compare the value to a different map based on key .
My struct also varies as per the different response so i want to have a map where i can get all the key value pairs for easy comparison . If someone has a suggestion to this please let me know .
You can use mapstructure package.
Sample of usage :
package main
import (
"fmt"
"github.com/mitchellh/mapstructure"
)
func main() {
type Emails struct {
Mail []string
}
type Person struct {
Name string
Age int
Emails Emails
Extra map[string]string
}
// This input can come from anywhere, but typically comes from
// something like decoding JSON where we're not quite sure of the
// struct initially.
mails := []string{"foo#bar.com", "foo2#bar.com"}
input := Person{
Name: "foo",
Age: 25,
Emails: Emails{Mail: mails},
Extra: map[string]string{"family": "bar"},
}
result := map[string]interface{}{}
err := mapstructure.Decode(input, &result)
if err != nil {
panic(err)
}
fmt.Printf("%#v", result)
}
playground
Given an object type that has functions as values I need to create object types with similar structure but having values as async functions.
Imagine this function
function convert(foo) {
return async function(...args) {
return foo(...args)
}
}
Applied to values of this type
type Source = {
foo: number => string,
bar: string => number
}
So I get this type
type Result = {
foo: number => Promise<string>,
bar: string => Promise<number>
}
Is it possible in flow?
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)
)
}
}
This question already has answers here:
Raw pointer turns null passing from Rust to C
(1 answer)
How to stop memory leaks when using `as_ptr()`?
(1 answer)
Closed 5 years ago.
I am porting some code to Rust that reads a database path from stdin, opens the database and loops for queries. I have done something similar in C so I am pretty sure that the problem is my non-understanding of Rust FFI.
I am using the sqlite3 binding provided by libsqlite3-sys, the one from rusqlite. The whole code is here.
open_connection initializes a pointer and passes it to sqlite3_open_v2 and checks if everything went well.
// Line 54 of complete code
fn open_connection(s: String) -> Result<RawConnection, SQLite3Error> {
unsafe {
let mut db: *mut sqlite3::sqlite3 = mem::uninitialized();
let r = sqlite3::sqlite3_open_v2(CString::new(s).unwrap().as_ptr(),
&mut db,
sqlite3::SQLITE_OPEN_CREATE |
sqlite3::SQLITE_OPEN_READWRITE,
ptr::null());
match r {
sqlite3::SQLITE_OK => Ok(RawConnection { db: db }),
_ => return Err(SQLite3Error::OpenError),
}
}
}
I create an SQL statement by converting the query from a Rust String to a C String, creating another pointer for the location of the statement itself and I go on creating a statement and checking the output:
// Line 35 of complete code
fn create_statement(conn: &RawConnection, query: String) -> Result<Statement, SQLite3Error> {
let len = query.len();
let raw_query = CString::new(query).unwrap().as_ptr();
unsafe {
let mut stmt: *mut sqlite3::sqlite3_stmt = mem::uninitialized();
if stmt.is_null() {
println!("Now it is null!");
}
match sqlite3::sqlite3_prepare_v2(conn.db,
raw_query,
len as i32,
&mut stmt,
ptr::null_mut()) {
sqlite3::SQLITE_OK => Ok(Statement { stmt: stmt }),
_ => Err(SQLite3Error::StatementError),
}
}
}
I try to execute a statement
// Line 81 of complete code
fn execute_statement(conn: &RawConnection, stmt: Statement) -> Result<Cursor, SQLite3Error> {
match unsafe { sqlite3::sqlite3_step(stmt.stmt) } {
sqlite3::SQLITE_OK => Ok(Cursor::OKCursor),
sqlite3::SQLITE_DONE => Ok(Cursor::DONECursor),
sqlite3::SQLITE_ROW => {
let n_columns = unsafe { sqlite3::sqlite3_column_count(stmt.stmt) } as i32;
let mut types: Vec<EntityType> = Vec::new();
for i in 0..n_columns {
types.push(match unsafe { sqlite3::sqlite3_column_type(stmt.stmt, i) } {
sqlite3::SQLITE_INTEGER => EntityType::Integer,
sqlite3::SQLITE_FLOAT => EntityType::Float,
sqlite3::SQLITE_TEXT => EntityType::Text,
sqlite3::SQLITE_BLOB => EntityType::Blob,
sqlite3::SQLITE_NULL => EntityType::Null,
_ => EntityType::Null,
})
}
Ok(Cursor::RowsCursor {
stmt: stmt,
num_columns: n_columns,
types: types,
previous_status: sqlite3::SQLITE_ROW,
})
}
x => {
println!("{}", x);
return Err(SQLite3Error::ExecuteError);
}
}
}
This always fails with the error code 21 MISUSE. Usually, this error happens when you try to execute a NULL statement but I have no idea how to figure it out.
Do you see any other problem that may cause a code 21 MISUSE?