Skip to content

← Generics and Traits step 5 of 24

Medium Primitives

Build a generic stack with conditional methods

Complete a stack that can store any element type. Its basic operations should need no trait bounds; only its text-rendering method requires Display.

pub struct Stack<T> {
    items: Vec<T>,
}

Keep the supplied new, push, pop and len methods. Add:

  • pub fn is_empty(&self) -> bool for every T.
  • Default for Stack<T> for every T, producing an empty stack.
  • pub fn render(&self) -> String in an impl<T: Display> Stack<T> block. Join the elements' display text from bottom to top with commas, without brackets or extra spaces. For an empty stack, return an empty string.

Then complete the command interpreter:

pub fn run(ops: Vec<(String, i64)>) -> Vec<String>

run starts with an empty Stack<i64> and returns one string per operation. Only push uses the numeric argument; every other operation ignores it.

Operation Effect Output
push Push the argument onto the top. len=<new length>
pop Remove the top value, if present. pop=<value> or pop=none
render Read the stack without changing it. The result of render wrapped in [ and ]
empty Read whether the stack is empty. empty=true or empty=false
Any other name Leave the stack unchanged. ?

Operation names are case-sensitive. Values span the full i64 range. An empty operation list returns an empty vector, and popping an empty stack must not panic.

For example:

operations: [("push", 5), ("push", -3), ("render", 0), ("pop", 0), ("render", 0)]
output:     ["len=1",     "len=2",      "[5,-3]",       "pop=-3",   "[5]"]

Separate the methods by their requirements

In impl<T> Stack<T>, the first <T> declares the block's type parameter. Stack<T> then uses that parameter. Methods in this block work for any element type, including types that implement neither Default nor Display.

In impl<T: Display> Stack<T>, the bound makes render available when elements implement Display. Keep the bound on this block rather than placing it on the struct or all its methods. Storing a value does not require knowing how to print it.

Stack<i64> and Stack<String> are distinct types. They share the generic definition, but you cannot assign one to a variable of the other type. An impl Stack<i64> block would add methods only for integer stacks, so it would not meet the generic requirements here.

Default without an element bound

Write Default manually and delegate to Self::new(). An empty vector contains no elements, so constructing it does not need a default value of T.

For this struct, #[derive(Default)] adds a T: Default bound. That would unnecessarily prevent callers from using Stack::<NotDefault>::default(). The manual implementation should use impl<T> Default for Stack<T> with no bound on T.

Completing the starter

The starter has working storage methods, but run handles only push. It also imports Display before using it. Add the missing operations and conditional rendering method, then provide Default and is_empty.

The Clippy gate checks this public API with new_without_default and len_without_is_empty. These checks encourage familiar method pairs; they do not verify stack order or whether your generic bounds are minimal. Check those requirements too, including a stack of a type without Default or Display.