Definition og implementering af traits
Behersk traits til at definere fælles adfærd på tværs af forskellige typer, på samme måde som interfaces i andre sprog.
Definition og implementering af traits er en gratis Lær at programmere i Rust-lektion på CoddyKit. Dette er lektion 2 af 3. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Lær at programmere i Rust, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Lær at programmere i Rust-kurset indeholder 3 lektioner i alt.
Hvad er traits i Rust?
I Rust er traits en effektiv måde at definere fælles funktionalitet på tværs af forskellige typer. Tænk på dem som grænseflader i andre sprog.
Et trait fortæller Rust, at en bestemt type har en given funktionalitet. Hvis en type implementerer et trait, betyder det, at typen leverer de metoder, der er defineret af traitet.
- Traits muliggør polymorfi: Du kan arbejde med forskellige typer på en ensartet måde.
- De er centrale for Rusts typesystem og sikre abstraktioner.
Erklæring af dit første trait
Du definerer et trait ved hjælp af nøgleordet trait. Inde i traitet oplister du metodesignaturer (navne, parametre og returtyper), som enhver type, der implementerer traitet, skal levere.
Lad os definere et enkelt trait ved navn Summary for elementer, der kan opsummeres.
trait Summary {
fn summarize(&self) -> String;
}
fn main() {
// Traits are definitions, not directly executable.
// We will implement and use them in later scenes!
println!("Trait 'Summary' defined.");
}Implementering af traits for typer
For at få en type til at bruge et trait anvender du syntaksen impl Trait for Type. Derefter leverer du den konkrete implementering af hver metode, der er defineret i traitet.
Her implementerer vi Summary for en struct ved navn NewsArticle.
trait Summary {
fn summarize(&self) -> String;
}
struct NewsArticle {
headline: String,
location: String,
author: String,
content: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
fn main() {
let article = NewsArticle {
headline: String::from("Penguins win Stanley Cup!"),
location: String::from("Pittsburgh, PA"),
author: String::from("Iceburgh"),
content: String::from("The Pittsburgh Penguins have won..."),
};
println!("New article summary: {}", article.summarize());
}Brug af funktioner med trait-begrænsninger
Når en type implementerer et trait, kan du skrive funktioner, der accepterer enhver type, som implementerer dette trait. Det giver fleksibel og generisk kode.
Funktionen notify kan modtage enhver type, der implementerer Summary.
trait Summary {
fn summarize(&self) -> String;
}
struct NewsArticle {
headline: String,
location: String,
author: String,
content: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
struct Tweet {
username: String,
content: String,
reply: bool,
retweet: bool,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
fn notify(item: &impl Summary) { // impl Summary is syntax sugar for a trait bound
println!("Breaking news! {}", item.summarize());
}
fn main() {
let tweet = Tweet {
username: String::from("horse_ebooks"),
content: String::from("of course, as you probably already know, people"),
reply: false,
retweet: false,
};
let article = NewsArticle {
headline: String::from("Rust is awesome!"),
location: String::from("Internet"),
author: String::from("Rustacean"),
content: String::from("Rust's type system is amazing."),
};
notify(&tweet);
notify(&article);
}Traits med standardfunktionalitet
Traits kan også levere standardimplementeringer af deres metoder. Det betyder, at typer ikke behøver at implementere alle metoder, hvis der findes en standardimplementering.
De kan vælge at bruge standardimplementeringen eller tilsidesætte den med deres egen logik.
trait Summary {
fn summarize_author(&self) -> String; // New required method
fn summarize(&self) -> String { // Default implementation
format!("(Read more from {})", self.summarize_author())
}
}
struct Tweet {
username: String,
content: String,
reply: bool,
retweet: bool,
}
impl Summary for Tweet {
fn summarize_author(&self) -> String { // Must implement required method
format!("@{}", self.username)
}
// We are using the default summarize() method here!
}
fn main() {
let tweet = Tweet {
username: String::from("dog_lover"),
content: String::from("My dog is the best!"),
reply: false,
retweet: false,
};
println!("Tweet summary: {}", tweet.summarize());
}Generiske funktioner med traits
Når du skriver generiske funktioner eller structs, kan du bruge trait-begrænsninger til at angive, at en generisk typeparameter T skal implementere et bestemt trait.
Det sikrer, at metoderne fra dette trait er tilgængelige for T.
trait Displayable {
fn display(&self);
}
struct Point<T> {
x: T,
y: T,
}
// Implement Displayable for Point<i32>
impl Displayable for Point<i32> {
fn display(&self) {
println!("Point: ({}, {})", self.x, self.y);
}
}
// Generic function that works for any type T that implements Displayable
fn print_item<T: Displayable>(item: T) {
item.display();
}
fn main() {
let p = Point { x: 10, y: 20 };
print_item(p);
// This function only works for types that implement Displayable
// let s = String::from("Hello");
// print_item(s); // This would cause a compile-time error
}Kombination af flere trait-begrænsninger
Nogle gange skal en generisk type implementere mere end ét trait. Du kan angive flere trait-begrænsninger ved hjælp af operatoren +.
I mere komplekse scenarier, især med mange generiske parametre, kan where-klausulen gøre koden mere overskuelig.
use std::fmt::Debug;
trait Printable {
fn print_info(&self);
}
struct Book {
title: String,
pages: u32,
}
impl Printable for Book {
fn print_info(&self) {
println!("Book: '{}' ({} pages)", self.title, self.pages);
}
}
impl Debug for Book { // Book also implements Debug trait
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Book {{ title: {}, pages: {} }}", self.title, self.pages)
}
}
// A function that requires its argument to be both Printable and Debug
fn process_item<T: Printable + Debug>(item: T) {
item.print_info();
println!("Debug info: {:?}", item);
}
fn main() {
let my_book = Book {
title: String::from("The Rust Book"),
pages: 600,
};
process_item(my_book);
}Hvad betyder det at returnere typer med `impl Trait`
Syntaksen impl Trait er ikke kun til funktionsparametre; den kan også bruges på returpositioner. Det er nyttigt, når du vil returnere en type, der implementerer et bestemt trait, men ikke ønsker at eksponere dens nøjagtige konkrete type.
Det forenkler funktionssignaturer og holder din API fleksibel.
trait Greeter {
fn greet(&self) -> String;
}
struct FriendlyGreeter;
impl Greeter for FriendlyGreeter {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
struct FormalGreeter;
impl Greeter for FormalGreeter {
fn greet(&self) -> String {
String::from("Greetings and salutations.")
}
}
// This function returns *some* type that implements Greeter
// The caller doesn't need to know if it's FriendlyGreeter or FormalGreeter
fn get_greeter(formal: bool) -> impl Greeter {
if formal {
FormalGreeter
} else {
FriendlyGreeter
}
}
fn main() {
let casual_greet = get_greeter(false);
println!("{}", casual_greet.greet());
let formal_greet = get_greeter(true);
println!("{}", formal_greet.greet());
}Byg videre på supertraits
Nogle gange kan ét trait logisk afhænge af et andet. Rust giver dig mulighed for at definere supertraits, hvilket betyder, at et trait kan kræve, at enhver type, der implementerer det, også implementerer et bestemt andet trait.
Det gør du med syntaksen trait SubTrait: SuperTrait. Det svarer til at sige: "Hvis du kan gøre X, skal du også kunne gøre Y".
trait Printable {
fn print_content(&self);
}
// Debug is a supertrait of PrintableDebug
// Any type implementing PrintableDebug must also implement Debug
trait PrintableDebug: Printable + std::fmt::Debug {
fn print_debug_and_content(&self) {
self.print_content();
println!("Debug info: {:?}", self);
}
}
struct Item {
id: u32,
name: String,
}
impl Printable for Item {
fn print_content(&self) {
println!("Item ID: {}, Name: {}", self.id, self.name);
}
}
impl std::fmt::Debug for Item {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Item {{ id: {}, name: {} }}", self.id, self.name)
}
}
// Now we can implement PrintableDebug because Item implements both Printable and Debug
impl PrintableDebug for Item {}
fn main() {
let my_item = Item {
id: 101,
name: String::from("Widget A"),
};
my_item.print_debug_and_content();
}Test din viden om traits
Se på følgende Rust-kodestykke. Hvilket af følgende udsagn om traits og trait-grænser er SANDT?
Traits: Dit værktøjssæt til adfærd
Vi har gennemgået Rusts kraftfulde trait-system!
- Traits definerer et sæt metoder, som en type kan implementere, og fungerer som interfaces.
- Du implementerer traits for bestemte typer ved hjælp af
impl Trait for Type. - Trait-grænser (
T: Trait) gør det muligt for generiske funktioner og structs at fungere med alle typer, der implementerer et påkrævet trait. - Traits kan have standardimplementeringer, og du kan angive flere trait-grænser (
T: Trait1 + Trait2) eller bruge supertraits. impl Traitkan også bruges på returpositioner til at skjule konkrete typer.
Traits er grundlæggende, når du skriver fleksibel, genanvendelig og sikker Rust-kode!
Lær Rust med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 39
- Lektioner
- 144
Ofte stillede spørgsmål
Er lektionen “Definition og implementering af traits” gratis?
Ja — alle 3 lektioner i læringssporet Lær at programmere i Rust, inklusive “Definition og implementering af traits”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Lær at programmere i Rust-kurset indeholder 3 lektioner i alt.
Hvad lærer jeg i “Definition og implementering af traits”?
Behersk traits til at definere fælles adfærd på tværs af forskellige typer, på samme måde som interfaces i andre sprog. Du øver dig i Lær at programmere i Rust med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Lær at programmere i Rust?
Der kræves ingen tidligere erfaring. Lær at programmere i Rust på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 3.
Hvor lang tid tager lektionen “Definition og implementering af traits”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Lær at programmere i Rust-lektion?
Ja. Alle Lær at programmere i Rust-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Skrivning af generisk kode i Rust
- Definition og implementering af traits
- Avanceret brug af traits: associated types