Exercise: Builder Type
In this example, we will implement a complex data type that owns all of its data. We will use the “builder pattern” to support building a new value piece-by-piece, using convenience functions.
Fill in the missing pieces.
#[derive(Debug)] enum Language { Rust, Java, Perl, } #[derive(Clone, Debug)] struct Dependency { name: String, version_expression: String, } /// A representation of a software package. #[derive(Debug)] struct Package { name: String, version: String, authors: Vec<String>, dependencies: Vec<Dependency>, language: Option<Language>, } impl Package { /// Return a representation of this package as a dependency, for use in /// building other packages. fn as_dependency(&self) -> Dependency { todo!("1") } } /// A builder for a Package. Use `build()` to create the `Package` itself. struct PackageBuilder(Package); impl PackageBuilder { fn new(name: impl Into<String>) -> Self { todo!("2") } /// Set the package version. fn version(mut self, version: impl Into<String>) -> Self { self.0.version = version.into(); self } /// Set the package authors. fn authors(mut self, authors: Vec<String>) -> Self { todo!("3") } /// Add an additional dependency. fn dependency(mut self, dependency: Dependency) -> Self { todo!("4") } /// Set the language. If not set, language defaults to None. fn language(mut self, language: Language) -> Self { todo!("5") } fn build(self) -> Package { self.0 } } fn main() { let base64 = PackageBuilder::new("base64").version("0.13").build(); println!("base64: {base64:?}"); let log = PackageBuilder::new("log").version("0.4").language(Language::Rust).build(); println!("log: {log:?}"); let serde = PackageBuilder::new("serde") .authors(vec!["djmitche".into()]) .version(String::from("4.0")) .dependency(base64.as_dependency()) .dependency(log.as_dependency()) .build(); println!("serde: {serde:?}"); }