Accessing struct field by a given string - struct

Assume we have a Person struct and a PersonName struct that used by a field, like the following:
struct Person {
name: PersonName,
age: u8,
}
struct PersonName {
name: String,
middle_name: Option<String>,
surname: Option<String>,
}
Create the struct:
let foo = Person { name: PersonName{name: String::from("bar"), middle_name: None, surname: String::from("baz") }, age: 16u8 };
How can I use this string to gain access to the struct field specified in it?
let foo_surname = eval("name.surname") // returns "baz"
// struct evaluator returns the value of specific field
// i.e.: field_path: "my_struct_field.sub_field1.sub_sub_field_1"
fn eval(field_path: String) -> ...{}
Is there any way to do this without writing a custom evaluator?

There is no way to do this.
Rust is a statically compiled language where this kind of information doesn't existing anymore during the program runtime.

Related

Write a struct with variable content as the field name

I need to write a bunch of struct with similar name within it. Such as:
pub struct ContactUpdate {
pub full_name: String,
pub full_address: String,
/// .... many other fields
}
pub struct Contact {
pub contact_id: Option<ObjectId>,
pub full_name: String,
pub full_address: String,
pub created_at: DateTime
/// .... many other fields
}
/// ... other structs with similar content/field name
I'm lazy. So instead of hard-coding each field name by hand, I think how can I make the field name of the struct into contants with fewer characters so I don't have to type as much. Also with other benefits to export that constants and use it in other files that need it.
pub const ID: &str = "contact_id";
pub const NAME: &str = "full_name";
pub const TIME: &str = "created_at";
pub const ADDR: &str = "full_address";
pub struct Contact {
pub ID: Option<ObjectId>,
pub NAME: String,
pub ADDR: String,
pub TIME: DateTime
/// .... many other fields
}
pub struct ContactUpdate {
pub NAME: String,
pub ADDR: String,
/// .... many other fields
}
Is this possible?
No. It is impossible.
If you really want (don't!) you can have a macro for that.
However, the entire reason for the existence of field names is for the programmers to know what they mean. If you want to use them as constants, you just give them no meaning and can get rid of them completely. At that time, you're back to the old days where variable name lengths were limited to 7-8 characters and people used all sorts of bad and unreadable codes to work with that. Don't do that.

Rust macro for creating new struct from struct

Is there any way to create a macro in rust that returns a new struct with the fields that I want. I have been working on this for weeks and cannot find a suitable solution. Seeing the problem below, is this even possible? And if so, how would I go about starting to implement this?
struct Person {
id: u32,
first_name: String,
middle_name: Option<String>,
last_name: String,
age: u8,
}
// I want to be able to have a function-like macro
// that takes the new name, old struct, and fields I want as input
// and returns a new struct based on that
sub_struct!("PersonNames", &Person, vec!["first_name", "middle_name", "last_name"])
// the above code yields the following
struct PersonNames {
first_name: String,
middle_name: Option<String>,
last_name: String,
}
another solution I can think of trying that I haven't yet is using derive-like macros.
#[sub_struct(&Person, vec!["first_name", "middle_name", "last_name"])]
struct PersonName {}

How to access field from strict serde_json deserialization with tags

I get Some(Address{...}) in the Address field of data which is expected. I can see it from the debug printout.
But &data.Address = &data.Address.unwrap();
gives me static error saying: "no field Address on type Profile".
let mut data:Profile = serde_json::from_str(json_string).unwrap();
&data.Address = &data.Address.unwrap();
#[derive(Deserialize, Debug, Serialize)]
#[serde(tag = "m_tag")]
pub enum Profile {
Data{
#[serde(rename="FirstName")] // to comply with Rust coding standards
first_name:String,
LastName: String,
Age: u32,
Address: Option<Address>,
PhoneNumbers: Vec<String>
},
Data2{
#[serde(rename="FirstName")] // to comply with Rust coding standards
first_name:String,
LastName: String,
Age: u32,
},
}
#[derive(Serialize, Deserialize)]
struct Address {
Street: String,
City: String,
Country: String
}
UPDATED:
Q1) I can unwrap the value BUt I would like to update the &data.Address to it.
But i'm back to original problem where &data is a Profile type and doesn't have the Address field.
match &data {
Profile::Data {
first_name,
LastName,
Age,
Address,
PhoneNumbers,
} => data.Address = &Address.as_ref().unwrap(),
Profile::Data2 {
first_name,
LastName,
Age,
} => println!("ok"),
}
Edit:
There's no need to create an Enum if the fields overlap. It's better to declare the fields that are not guaranteed to be there as optional:
#[derive(Deserialize, Debug, Serialize)]
struct Profile {
#[serde(rename="FirstName")]
first_name:String,
LastName: String,
Age: u32,
Address: Option<Address>,
PhoneNumbers: Option<Vec<String>>
};
So that now it's guaranteed that the type Profile has an Address field:
let data:Profile = serde_json::from_str(json_string).unwrap();
println!("{:?}", data.Address);
Here's a code example working in the Rust Playground.

How does one map a struct to another one in Rust?

In others languages (like Java), libraries are available for mapping object fields to another object (like mapstruct). It is useful indeed for isolating controller and service from each other.
PersonDto personDto = mapper.businessToDto(personBusiness);
And I can't find how to do it with Rust ? I didn't find any libraries helping with this, or any way to do it. Any resource would be very appreciated !
In rust you usually do it via From trait:
struct Person {
name: String,
age: u8,
}
struct PersonDto {
name: String,
age: u64,
}
impl From<Person> for PersonDto {
fn from(p: Person) -> Self {
Self {
name: p.name,
age: p.age.into(),
}
}
}
So you can make an Into conversion:
let person = Person { name: "Alex".to_string(), age: 42 };
let person_dto: PersonDto = person.into();
// or via an explicit `T::from:
let person_dto = PersonDto::from(person);

How to restrict the construction of struct?

Is it possible to forbid creating an instances directly from member initialization?
e.g.
pub struct Person {
name: String,
age: u8,
}
impl Person {
pub fn new(age: u8, name: String) -> Person {
if age < 18 {
panic!("Can not create instance");
}
Person { age, name }
}
}
I can still use
Person {
age: 6,
name: String::from("mike")
}
to create instance. Is there anyway to avoid this?
Answer to question
You cannot create that struct from member initialization, because members are by default private and cannot be used directly. Only the immediate module and its submodules can access private fields, functions, ... (see the book about visibility).
Your example works, because your function is in that certain scope.
mod foo {
pub struct Person {
name: String,
age: u8,
}
impl Person {
pub fn new(age: u8, name: String) -> Person {
if age < 18 {
panic!("Can not create instance");
}
Person { age, name }
}
}
}
use foo::Person; // imagine foo is an external crate
fn main() {
let p = Person {
name: String::from("Peter"),
age: 8,
};
}
(Playground)
error[E0451]: field `name` of struct `Person` is private
error[E0451]: field `age` of struct `Person` is private
Make it possible to create a struct from the outside
On the other hand, if you want to make it possible to create an instance by member initialization, use the pub keyword in front of all members.
pub struct Person {
pub name: String,
pub age: u8,
}
Make it possible to access the fields, but not creating a struct from the outside
Please see KittenOverflows answer for a better approach to this.
--
Sometimes it's useful to let the user of your crate access the members directly, but you want to restrict the creation of an instance to your "constructors". Just add a private field.
pub struct Person {
pub name: String,
pub age: u8,
_private: ()
}
Because you cannot access _private, you cannot create an instance of Person directly.
Also the _private field prevents creating a struct via the update syntax, so this fails:
/// same code from above
fn main() {
let p = Person::new(8, String::from("Peter"));
let p2 = Person { age: 10, ..p };
}
error[E0451]: field `_private` of struct `foo::Person` is private
--> src/main.rs:27:34
|
27 | let p2 = Person { age: 10, ..p };
| ^ field `_private` is private
For Rust >= 1.40.0, consider applying the non_exhaustive attribute to your struct.
// Callers from outside my crate can't directly construct me
// or exhaustively match on my fields!
#[non_exhaustive]
pub struct Settings {
pub smarf: i32,
pub narf: i32,
}
More info in the 1.40.0 release notes.

Resources