First approach towards value object crate

Hey community,

Working on a crate for simple value objects, which is highly inspired by the code examples of the FSharp book Domain Modeling Made Functional.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0bdc195c69b43669d7f3ec84015ff3a3

Feedback and thoughts appreciated :slight_smile:

Not all of the parameters in fn new_string_like(...) require an explicit lifetime, only those that may outlive the scope of the function. In this case, only the str parameter requires an explicit lifetime because the others are only used in the function body rather than being returned from it. This lets us clean up the definition of EmailAddress::new and fn new_string_like(...).

Rather than StringCtor<'a, T> using dynamic dispatch, we can use a generic parameter to specify a function with a specific type signature. If I understand correctly, this lets the compiler inline the function during compilation rather than following the pointer at runtime, giving better performance.

On that note, new_string_like can never return a value of Ok(None), so either CtorResult doesn't need to wrap an Option or the generic function parameter should be Fn(&'a str) -> Option<T> to allow the caller to successfully return nothing.

When using the format!() macro, it isn't necessary to call .to_string(), as the macro does this for us.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ef7ce666db85ea780ade218465399634

I don't do a lot work with string slices though, so others may have better input on the actual task at hand.

Thanks for the feedback and suggestions @frsrblch.

I will look into the dynamic dispatch.

Made it into a (my first) crate:

Thanks for the tip, applied the changes.
Moved to static dispatch and removed some lifetimes.

Another note: you generally want to keep .unwrap() use to a minimum. Rather than checking for None and then unwrapping, it is preferable to handle both possibilities at once. Function chaining and pattern matching are available for safely and ergonomically handling different possibilities.

This isn't immediately intuitive if you're coming from a different language, but it removes the implicit assumption that the None case has been handled prior to .unwrap() being called. It moves some of the burden of ensuring correctness from the programmer to the compiler.

Pattern matching: https://learning-rust.github.io/docs/e3.option_and_result.html
Functional methods: https://doc.rust-lang.org/std/option/enum.Option.html

1 Like