Rust newbie here. Let me start with some context before giving a minimal example. I am creating a library crate with some time-related functionality among other things. I have a Time struct which consists of fields for various units:
pub struct Time {
seconds: Second,
minutes: Minute,
hours: Hour,
days: Day,
weeks: Week,
}
Note that each of the units has its own type. I realize there are a number of ways I could approach this using enums and traits, and I may very well change my approach later, but I still want to know how to do what I'm trying to do.
I wrote a working FromStr implementation for Time that reads from a config file and generates the corresponding Time instance. An example time string would be "1s2m3h" (1 second, 2 minutes, 3 hours). I'm now trying to refactor things and make the code a bit more modular. I want Time's FromStr to be implemented as a sum over FromStr's on each of the individual unit types. That's fairly straightforward, but there is a lot of repetition, as the logic is essentially unchanged between them, which the only difference between the qualifier character that denotes which unit of time we're working with.
Each of the unit types implements a trait I called Unit. I did this to essentially require common functionality across unit types (get type, get value, get qualifier, add to time, from string, etc.). What I would like is to implement FromStr on the Unit trait, such that Unit::from_str() returns a trait object, and the Time FromStr implementation can just sum all of the units without caring which types they are. After struggling with it for a while, I'm still pretty lost.
Here is a minimal example:
use core::str::FromStr;
fn main() {
//
// A is a trait. I want A::from_str(s) to instantiate a trait object whose
// underlying type is based on the string argument. In this minimal example,
// the string is ignored, but in the real problem, I would of course use the
// string argmuent to inform the output.
//
let _a = <&dyn A>::from_str("ignored string").unwrap();
}
//
// A - This is playing the role of the Unit trait described above.
//
trait A {}
#[derive(Debug)]
struct ParseAError;
//
// Not sure if &dyn A is the right syntax here. I'm still trying to make sense
// of trait objects.
//
impl FromStr for &dyn A {
type Err = ParseAError;
fn from_str(_s: &str) -> Result<Self, Self::Err> {
let thing_b = B::default();
//
// Here is where I want to return the underlying B instance as an A
// trait object. This of course does not compile as written. I think I
// need to use Box and/or Arc, but nothing I tried works.
//
Ok(&thing_b)
}
}
//
// B - This is playing the role of the individual types (Second, Minute, etc.)
//
#[derive(Debug, Default)]
struct B;
impl A for B {}
#[derive(Debug)]
struct ParseBError;
//
// This implementation is straightforward, but I want to use the more generic
// Unit one above.
//
impl FromStr for B {
type Err = ParseBError;
fn from_str(_s: &str) -> Result<Self, Self::Err> {
Ok(B::default())
}
}
Any help is much appreciated. Thanks!