I’ve spent roughly 4 hours in one sitting trying to figure out this quiz, making all those notes, and I figured that it might make sense to publish them, since I already have them.
Question 1
Requirement: The API client is expected to provide a single WidgetImpl enum representing all possible widgets, and implement the Widget trait for WidgetImpl.
Question 2
Requirement: The API client is expected to provide a unique struct for each possible widget, and implement the Widget trait for each struct. Each widget can return a vector containing widgets of any possible type.
Question 3
Requirement: Only types that implement the Widget trait should be returned from render.
Data Structures
str...
I’ve spent roughly 4 hours in one sitting trying to figure out this quiz, making all those notes, and I figured that it might make sense to publish them, since I already have them.
Question 1
Requirement: The API client is expected to provide a single WidgetImpl enum representing all possible widgets, and implement the Widget trait for WidgetImpl.
Question 2
Requirement: The API client is expected to provide a unique struct for each possible widget, and implement the Widget trait for each struct. Each widget can return a vector containing widgets of any possible type.
Question 3
Requirement: Only types that implement the Widget trait should be returned from render.
Data Structures
struct Container;
struct TextField;
struct Button;
or
enum WidgetImpl {
Container,
TextField,
Button,
}
Associated type
pub trait Widget {
type Children: Widget;
fn render(&self) -> Vec<Self::Children>;
}
This means that every type that implements Widget should choose a specific type of Children that also implements Widget. At compile time there should be a single concrete type for Children. My confusion at first here was that the Children can be different types in one implementation as long as they implement the Widget, for example: type Children = Box; This gives an error: the value of the associated type Children in Widget must be specified
impl Widget for WidgetImpl {
type Children = WidgetImpl;
fn render(&self) -> Vec<Self::Children> {
vec![WidgetImpl::Container, WidgetImpl::TextField]
}
}
Answer 1: Initially I answered that this works because Children is required to be one specific type, but the problem with that is that the Children do not necessarily need to be of the provided enum type, they could be of any type that implements the Widget, so it’s not enforcing the client to use the enum as the only source of Widgets
Answer 2: This does not work, because Children can be any type that implements Widget, but only a single type, meaning that this part of the question “can return a vector containing widgets of any possible type” would not be true. Children can only be of one specific type either Button or TextField for example but you can’t have a vector that will contain both Button and a TextField
Answer 3: This works, because associated type should implement Widget, and it’s returned from the render, so that means that whatever render returns is going to implement the Widget
Sized Self
pub trait Widget: Sized {
fn render(&self) -> Vec<Self>;
}
This trait means that everyone who implements it will be able to only use their own type in the render. The difference with the associated type is that in associated type you can implement Container that renders Button as long as the button implements Widget. In this implementation you’ll only be able to render Container if you implement Container
Answer 1: This works because WidgetImpl has all of the possible widgets and the children is WidgetImpl itself
Answer 2: This does not work, because render is allowed to return only its own type
Answer 3: This works because whatever self is, it’s going to implement Widget and render always returns Self.
impl Widget for WidgetImpl {
fn render(&self) -> Vec<Self> {
vec![]
}
}
Generic Type Parameter
This is a generic trait with a type parameter. The children type parameter will be substituted for concrete type in the implementation Difference with the non-generic version is that you must specify the type for which you implement the trait and the difference with associated type trait is that you can have multiple implementation for one type with different generic type, for example: impl WidgetButton for Container impl WidgetTextField for Container
pub trait Widget<Children> {
fn render(&self) -> Vec<Children>;
}
Code for question 3 showing why it does not work:
impl Widget<i32> for i32 {
fn render(&self) -> Vec<i32> {
vec![3]
}
}
Answer 1: This does not work, because the API allows you to create multiple implementations for the WidgetImpl, this in turn lets the client add other widgets that are not included in the enum, but the requirement was to have all possible widgets in enum impl WidgetWidgetImpl for WidgetImpl … implement WidgetSomeOtherWidget for WidgetImpl …
Answer 2: This does not work for similar reasons as associated type. Each struct can be implemented even in a generic way to have as children all of the other structs, but children will always be a vector of one specific type
Answer 3: This does not work, because you can implement the Widget for any type. It does not constrain to the types that implement Widget
Trait Object References
This trait allows having as children any type that implements the Widget trait
impl Widget for Button {...}
impl Widget for TextField {...}
impl Widget for Widgets {
fn render(&self) -> Vec<&dyn Widget> {
vec![&Button, &TextField]
}
}
Answer 1: This does not work, because the API allows you to have any types for Children as long as they implement the Widget trait. The requirement is to provide a single WidgetImpl enum with all possible widgets
Answer 2: This works. Self-explanatory
Answer 3: This works because the render returns a vector of references that implement Widget
pub trait Widget {
fn render(&self) -> Vec<&dyn Widget>;
}
Boxed Trait Objects
The difference between boxed trait objects and the referenced trait objects is that in this implementation you own the Children and not reference them, meaning you can mutate them
pub trait Widget {
fn render(&self) -> Vec<Box<dyn Widget>>;
}
Answer 1: This does not work, because the API allows you to have any types for Children as long as they implement the Widget trait. The requirement is to provide a single WidgetImpl enum with all possible widgets
Answer 2: This works. Self-explanatory
Answer 3: This works because the render returns a vector of boxed values that implement the Widget trait