Skip to content

← Generics and Traits step 20 of 24

Hard Primitives

Implementing traits with a newtype

Give vectors a display format by wrapping them in a type defined in your crate. Repair the starter's invalid trait implementation.

pub struct Wrap(pub Vec<i32>);

pub fn render(rows: Vec<Vec<i32>>) -> Vec<String>

Implement Display for Wrap. Render each row using the inner vector's Debug format, with brackets and a comma followed by a space between values. Preserve row order. Empty input produces no strings; an empty row produces "[]".

[[1, 2, 3], [], [-1]] -> ["[1, 2, 3]", "[]", "[-1]"]
[]                    -> []

Delegate formatting with write!(f, "{:?}", self.0). In render, wrap each row and call to_string(). The standard library supplies ToString for types implementing Display.

Why the starter fails

The starter attempts impl Display for Vec<i32>. Both the trait and the type come from the standard library, so this implementation violates the orphan rule and produces E0117.

Imagine two unrelated libraries each supplying a different display format for Vec<i32>. A program using both would have conflicting implementations. Rust's coherence rules prevent ambiguous trait implementations. They include both orphan checks, which restrict where implementations may be written, and overlap checks, which reject implementations that apply to the same case.

A local type does not remove the overlap checks. Even a crate implementing its own trait must avoid conflicting implementations.

A wrapper is a distinct type

Wrap is a newtype: a tuple struct with one field. Because it is local, implementing the foreign Display trait for it is allowed. This does not change how bare vectors behave.

Moving a vector into the wrapper transfers ownership without cloning its elements or allocating another vector buffer. Formatting still allocates the resulting string. The ordinary struct declaration does not itself guarantee identical layout or ABI to its field; use repr(transparent) when that guarantee is needed and its requirements are satisfied.

A type alias would not help. type Wrap = Vec<i32> is another name for the existing type, not a new local type.

Foreign traits with type parameters

For a trait without type arguments, such as Display, a foreign trait needs a local implementing type. Traits with arguments have a broader rule: a local type can appear in an argument too.

For impl<P...> Trait<T1, ..., Tn> for T0, if the trait is foreign, consider T0, T1, ..., Tn in that order. At least one must be local, and no uncovered type parameter may appear before the first local type. A bare T is uncovered; ordinary type constructors can cover parameters without becoming local themselves. Fundamental types such as Box have special rules: Box<Local> counts as local, but Box<T> does not cover T.

For example, this complete conversion is valid:

struct Local<T>(T);

impl<T> From<T> for Local<T> {
    fn from(value: T) -> Self {
        Local(value)
    }
}

fn main() {
    let wrapped: Local<i32> = 7.into();
    assert_eq!(wrapped.0, 7);
}

By contrast, impl<T> From<Local<T>> for T fails with E0210: the bare T in the implementing-type position precedes the local trait argument. This is different from claiming that any generic From implementation for a local wrapper is forbidden.

Choose the wrapper's API

The wrapper does not automatically inherit the vector's methods. Access the inner vector through self.0, or add the methods and traits the wrapper's callers need.

Deref can enable method lookup on the inner type, but introduces broader pointer-like behavior. It does not erase the wrapper's distinct identity. This formatting exercise needs only Display.

If you later add conversions, prefer From<Vec<i32>> for Wrap. It automatically provides Into<Wrap> for the vector. Implementing Into<Wrap> for Vec<i32> directly is allowed by the orphan rule because Wrap is local, but does not automatically provide the corresponding From implementation.

A passing submission must compile, pass the tests, and pass clippy -D warnings.

References: trait coherence, E0210, and From conversions.