O padrão Newtype
Envolva tipos para obter segurança e clareza.
O padrão Newtype é uma aula grátis de Learn Rust Coding no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Learn Rust Coding, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Learn Rust Coding inclui 4 aulas no total.
Partes desta aula ainda não foram traduzidas e aparecem em inglês.
What Is a Newtype?
A newtype is a single-field tuple struct that wraps an existing type to give it a distinct identity. struct Meters(f64) is a brand new type even though it holds a plain f64.
The wrapper has zero runtime cost but lets the compiler enforce meaning that a raw primitive cannot.
struct Meters(f64);
struct Seconds(f64);Preventing Unit Mix-Ups
Raw primitives are easy to confuse. If both a distance and a time are f64, nothing stops you swapping them in a call.
Wrapping each in its own newtype makes such mistakes a compile error instead of a silent bug.
fn speed(d: Meters, t: Seconds) -> f64 {
d.0 / t.0
}
// speed(Seconds(2.0), Meters(10.0)) -> compile errorAccessing the Inner Value
You reach the wrapped value through tuple index .0. Many newtypes also expose a method or implement From for ergonomic conversions.
struct UserId(u64);
impl UserId {
fn value(&self) -> u64 { self.0 }
}
fn main() {
let id = UserId(42);
println!("id = {}", id.value());
}Encapsulating Invariants
Make the inner field private and validate in a constructor. Then any value of the newtype is guaranteed valid, so downstream code never re-checks.
pub struct Email(String);
impl Email {
pub fn new(s: &str) -> Option<Email> {
if s.contains('@') {
Some(Email(s.to_string()))
} else {
None
}
}
}The Orphan Rule
Rust forbids implementing a foreign trait for a foreign type. You cannot write impl Display for Vec<T> because you own neither the trait nor the type.
This rule keeps trait coherence sound across crates, but it can block useful impls.
Newtypes Bypass the Orphan Rule
Because the newtype is defined in your crate, you now own a local type and may implement any trait for it. This is the standard workaround for the orphan rule.
use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}A Runnable Wrapper Display
Here the wrapper from the previous scene is put to work. We own Wrapper, so implementing Display for it is allowed and the program prints the joined list.
use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
fn main() {
let w = Wrapper(vec!["a".into(), "b".into()]);
println!("{}", w);
}Restricting the API Surface
Wrapping a powerful type lets you expose only a safe subset. A NonEmptyVec can hide mutating methods that would let it become empty, preserving its invariant.
pub struct NonEmptyVec<T>(Vec<T>);
impl<T> NonEmptyVec<T> {
pub fn new(first: T) -> Self {
NonEmptyVec(vec![first])
}
pub fn first(&self) -> &T {
&self.0[0]
}
}Zero-Cost Abstraction
A newtype with one field has the same memory layout as the wrapped value. The compiler optimizes the wrapper away, so safety here is genuinely free at runtime.
Adding #[repr(transparent)] guarantees identical layout, which matters for FFI.
#[repr(transparent)]
struct Celsius(f64);Deriving Traits on Newtypes
Newtypes often derive standard traits so they behave like the inner value where appropriate. Deriving keeps them ergonomic for keys, comparisons, and debug output.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct ProductId(u32);
fn main() {
let a = ProductId(7);
let b = a.clone();
println!("{:?} == {:?}: {}", a, b, a == b);
}Newtype Versus Type Alias
Do not confuse a newtype with a type alias. type Meters = f64 is just a name; it is still an f64 and offers no extra safety.
A newtype struct Meters(f64) is a genuinely distinct type the compiler can keep separate.
type Km = f64; // alias: interchangeable with f64
struct Mi(f64); // newtype: distinct from f64Quick Check
Decide why a newtype helps where a type alias does not.
Recap
The newtype pattern wraps an existing type in a one-field tuple struct to gain a distinct identity at zero runtime cost. It prevents value mix-ups, encapsulates invariants behind a private field, and sidesteps the orphan rule so you can implement foreign traits.
Unlike a type alias, a newtype is a real, separate type.
Perguntas Frequentes
A aula “O padrão Newtype” é grátis?
Sim — o texto completo de “O padrão Newtype” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Learn Rust Coding, atualize para CoddyKit PRO. O curso de Learn Rust Coding inclui 4 aulas no total.
O que vou aprender em “O padrão Newtype”?
Envolva tipos para obter segurança e clareza. Você pratica Learn Rust Coding com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Learn Rust Coding?
Nenhuma experiência prévia é necessária. Learn Rust Coding no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.
Quanto tempo leva a aula “O padrão Newtype”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Learn Rust Coding?
Sim. Cada aula de Learn Rust Coding inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.