We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Generics and Traits step 22 of 24
Sealed traits: public to use, closed to implement
Repair the visibility of a sealed formatting trait. Other crates should be able to use its methods and trait bounds, while this crate controls which types implement it.
mod sealed {
pub trait Sealed {}
}
pub trait Format: sealed::Sealed {
fn render(&self, v: i64) -> String;
fn label(&self) -> &'static str { "fmt" }
}
A supertrait is a trait required by another trait. Here, implementing
Format also requires implementing sealed::Sealed. The private module
prevents an outside crate from naming that required trait, so an outside
type cannot implement Format through this API.
Preserve the formatting behavior
Keep Format, Hex, Binary, Decimal and render_all public. Each input
to render_all is a (tag, value) pair. Return one string for each pair,
preserving order. An empty input produces an empty output.
| Tag | Value | Output |
|---|---|---|
hex |
255 |
hex=0xff |
bin |
5 |
bin=0b101 |
dec |
42 |
fmt=42 |
HEX |
255 |
?=? |
| any other tag | any value | ?=? |
Tags are case-sensitive and are not trimmed. Hex and Binary override
label; Decimal inherits the default "fmt". Keep that default rather
than changing the decimal prefix to dec.
Hexadecimal and binary rendering use {:#x} and {:#b} on an i64.
They include the 0x or 0b prefix. For negative values these formatters
show the 64-bit two's-complement representation, so hexadecimal -5 becomes
hex=0xfffffffffffffffb. Decimal output uses a minus sign as usual.
Fix the visibility boundary
The starter declares the inner trait as private:
mod sealed {
trait Sealed {}
}
Code in the parent module cannot name that private child-module item.
References such as sealed::Sealed therefore produce E0603. Change the
declaration to pub trait Sealed {} and keep the enclosing module private.
The rest of the starter already implements the required formatting behavior.
The two visibility choices have separate effects:
- The public inner trait can be named by code in this crate that can access
its module, including the parent module where
Formatis defined. - The private module provides no public path to that trait for other crates.
Keep the seal out of public re-exports too. Making the module public, or
publicly re-exporting its trait, would let an outside crate implement the
seal for its own types and then implement Format.
A different example puts both traits in the same module:
trait Sealed {}
pub trait Format: Sealed {}
Here, name lookup is allowed, but the public trait has a less-visible bound.
The private_bounds lint reports that mismatch and becomes an error under
the warnings-as-errors gate. This differs from the starter's E0603.
Older references may call this E0445; current diagnostics use the lint.
Separate use from implementation
A downstream crate can import Format, call its methods on the supplied
types and write generic bounds such as T: Format. This particular trait
is also dyn-compatible, so it supports Box<dyn Format> as used in the
starter. Sealing alone does not make an arbitrary trait dyn-compatible.
The crate maintains the implementations of both the public trait and the seal. This control depends on the actual implementations: a blanket seal implementation for every type would defeat the intended restriction. Inside this crate, code with access to the seal can add implementations.
The grader compiles and runs a single program, so its output checks cannot by themselves demonstrate a downstream implementation failure. Preserve the requested module structure; the full visibility guarantee is best checked using a separate consuming crate.
Sealing does not make every API change compatible
Controlling implementations makes some changes easier: adding a required
method does not force outside implementors to update, because this API has
none. It can still break callers. For example, adding a generic method
without a Self: Sized restriction can make Format unusable as a trait
object. Adding methods can also introduce ambiguity with another trait's
methods, whether the new method has a default or not.
Review the exposed API and its uses before deciding whether a change is compatible. Sealing is not a substitute for that review, and a trait's implementors do not become an enum that the compiler can match exhaustively.
Use an open trait when outside crates should provide implementations, such as a plugin extension point. Use sealing when controlling implementations is part of the API's design. Neither choice is universally preferable.
Passing requires compilation, the expected outputs and Clippy with warnings treated as errors.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.