How does one map a struct to another one in Rust? - 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);

Related

Is this factory and how do I make constructor for struct?

struct Person{
name: String
}
fn person_factory(name: String)-> Person {
Person{
name,
}
}
fn main() {
let p1 = person_factory("Viktor Draganov".to_string());
println!("{}", p1.name);
}
Is this factory in rust? And how can I initialize person from constructor?
person_factory does look like a factory (if not a particularly useful or necessary one). Usually factories exist to abstract some creation logic away from the user. Such as converting a date of birth to an age when creating a person.
Rust does not have any special method that works like a constructor you'd be familiar with from languages like Java, Python, C++, etc; however, there is a common practice that provides essentially the same behavior as described in this site which covers Rust design patterns: Constructors.
Following this article you could make a "constructor" for your Person class like this (it should look VERY similar to the factory method you shared above):
struct Person{
name: String
}
impl Person {
pub fn new(name: String) -> Self {
Person {
name: name
}
}
}
fn main() {
let p1 = Person::new("Viktor Draganov".to_string());
println!("{}", p1.name);
}

Can I declare an enum value that takes a String or &str without needing additional functions?

I have an enum with a String:
enum MyLovelyEnum {
Thing(String),
}
For tests, I would like to be able to pass in a &'static str to avoid MyLovelyEnum::Thing("abc".to_string) over and over.
I found that you can do this nicely with structs with a constructor:
// From: https://hermanradtke.com/2015/05/06/creating-a-rust-function-that-accepts-string-or-str.html
struct Person {
name: String,
}
impl Person {
fn new<S: Into<String>>(name: S) -> Person {
Person { name: name.into() }
}
}
fn main() {
let person = Person::new("Herman");
let person = Person::new("Herman".to_string());
}
I know I can use lifetimes or Cow as described in What's the best practice for str/String values in Rust enums? or I can make my own function.
Is there something close to the example in the blog post for enums? e.g.
// this is the kind of thing I am after but this specifically is not correct syntax
enum MyLovelyEnum {
Thing<S: Into<String>>(S)
}
You can create a generic enum:
enum MyLovelyEnum<S>
where
S: Into<String>,
{
Thing(S),
}
MyLovelyEnum::Thing("a");
MyLovelyEnum::Thing("b".to_string());
I likely wouldn't do that in my code, instead opting to create a constructor, much like the blog post you linked:
enum MyLovelyEnum {
Thing(String),
}
impl MyLovelyEnum {
fn thing(s: impl Into<String>) -> Self {
MyLovelyEnum::Thing(s.into())
}
}
MyLovelyEnum::thing("a");
MyLovelyEnum::thing("b".to_string());

Accessing struct field by a given string

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.

Using PyAny to pass Rust-created objects from Python back to Rust

I have a struct + implementation in Rust that I return to Python. This object can also be passed back to Rust for further work. (In my actual code, I'm using a HashMap<String, MyStruct>, but even just using a struct directly seems to cause the same issues, so my example uses struct Person for simplicity.)
It appears that I need to impl FromPyObject for Person, but Rust can't find PyAny's downcast method
#[pyclass]
struct Person {
name: String,
age: u8,
height_cm: f32,
}
impl pyo3::FromPyObject<'_> for Person {
fn extract(any: &PyAny) -> PyResult<Self> {
Ok(any.downcast().unwrap())
^^^^^^^^ method not found in `&pyo3::types::any::PyAny`
}
}
#[pyfunction]
fn make_person() -> PyResult<Person> {
Ok(Person {
name: "Bilbo Baggins".to_string(),
age: 51,
height_cm: 91.44,
})
}
#[pyfunction]
fn person_info(py:Python, p: PyObject) -> PyResult<()> {
let p : Person = p.extract(py)?;
println!("{} is {} years old", p.name, p.age);
Ok(())
}
Is this the right way to pass a Rust object from Python back into Rust? If so, what's the right way to use PyAny here?

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