Can you explain which code is better and WHY? (It's just an example)
- simple.rs
#![allow(dead_code)]
use std::error::Error;
pub struct App {
pub port: u16,
}
pub struct Database {
pub url: String,
}
pub struct Config {
pub app_port: u16,
pub db_url: String,
}
impl Config {
pub fn build() -> Result<Self, Box<dyn Error>> {
let app_port: u16 = 8080;
let db_url: String = "connection".to_string();
let config = Self { app_port, db_url };
Ok(config)
}
}
impl App {
pub fn new(port: u16) -> Self {
Self { port }
}
}
impl Database {
pub fn new(url: String) -> Self {
Self { url }
}
}
- with_traits.rs (with From and Into traits)
#![allow(dead_code)]
use std::{error::Error, sync::Arc};
pub struct Config {
pub app_port: u16,
pub db_url: String,
}
pub struct App {
pub port: u16,
}
pub struct Database {
pub url: String,
}
impl Config {
pub fn build() -> Result<Arc<Self>, Box<dyn Error>> {
let app_port: u16 = 8080;
let db_url = "connection".to_string();
let config = Self { app_port, db_url };
Ok(Arc::new(config))
}
}
impl<T> From<T> for App
where
T: AsRef<Config>,
{
fn from(value: T) -> Self {
let port = value.as_ref().app_port;
Self { port }
}
}
impl<T> From<T> for Database
where
T: AsRef<Config>,
{
fn from(value: T) -> Self {
let url = value.as_ref().db_url.clone();
Self { url }
}
}
- main.rs
mod simple;
mod with_traits;
fn main() {
// version 1
let config = match simple::Config::build() {
Ok(config) => config,
Err(error) => {
println!("Config init: {error}");
return;
}
};
let _app = simple::App::new(config.app_port);
let _database = simple::Database::new(config.db_url);
// version 2
let config = match with_traits::Config::build() {
Ok(config) => config,
Err(error) => {
println!("Config init: {error}");
return;
}
};
let _app: with_traits::App = config.clone().into();
let _database: with_traits::Database = config.clone().into();
}