Rust
strukturer
Sök…
Syntax
- struct Foo {field1: Type1, field2: Type2}
- let foo = Foo {field1: Type1 :: new (), field2: Type2 :: new ()};
- struct Bar (Type1, Type2); // typ av tupel
- let _ = Bar (Type1 :: new (), Type2 :: new ());
- strukt Baz; // enhetsliknande typ
- låt _ = Baz;
- låt Foo {field1, ..} = foo; // extrahera fältet 1 efter mönstermatchning
- låt Foo {field1: x, ..} = foo; // extrahera fält1 som x
- let foo2 = Foo {field1: Type1 :: new (), .. foo}; // konstruera från befintliga
- impl Foo {fn fiddle (& self) {}} // deklarera instansmetod för Foo
- impl Foo {fn tweak (& mut self) {}} // deklarera mutable instansmetod för Foo
- impl Foo {fn double (self) {}} // förklara ägande instansmetod för Foo
- impl Foo {fn new () {}} // förklara tillhörande metod för Foo
Definiera strukturer
Strukturer i Rust definieras med struct
. Den vanligaste strukturen består av en uppsättning namngivna fält:
struct Foo {
my_bool: bool,
my_num: isize,
my_string: String,
}
Ovanstående förklarar en struct
med tre fält: my_bool
, my_num
och my_string
, av typen bool
, isize
respektive String
.
Ett annat sätt att skapa struct
i Rust är att skapa en tupelstruktur :
struct Bar (bool, isize, String);
Detta definierar en ny typ, Bar
, som har tre namngivna fält, av typ bool
, isize
och String
, i den ordningen. Detta kallas newtype mönster , eftersom det effektivt introducerar en ny "namn" för en viss typ. Men gör den det på ett mer kraftfullt sätt än alias som skapats med hjälp av type
nyckelordet; Bar
är här en helt funktionell typ, vilket betyder att du kan skriva dina egna metoder för det (nedan).
Slutligen, förklara en struct
utan fält, kallad en enhetliknande struktur :
struct Baz;
Detta kan vara användbart för att håna eller testa (när du vill implementera ett drag trivialt) eller som en markörtyp. I allmänhet är det dock osannolikt att du kommer att stöta på många enhetsliknande strukturer.
Observera att struct
i Rust är alla privata som standard --- det vill säga att de inte kan nås från koden utanför modulen som definierar typen. Du kan prefixa ett fält med pub
nyckelord för att göra det fältet allmänt tillgängligt. Dessutom är struct
själv privat. För att göra typen tillgänglig för andra moduler måste struct
också förinställas med pub
:
pub struct X {
my_field: bool,
pub our_field: bool,
}
Skapa och använda strukturvärden
Tänk på följande struct
:
struct Foo {
my_bool: bool,
my_num: isize,
my_string: String,
}
struct Bar (bool, isize, String);
struct Baz;
Att konstruera nya strukturvärden för dessa typer är enkelt:
let foo = Foo { my_bool: true, my_num: 42, my_string: String::from("hello") };
let bar = Bar(true, 42, String::from("hello"));
let baz = Baz;
Få åtkomst till fält för en struktur med .
:
assert_eq!(foo.my_bool, true);
assert_eq!(bar.0, true); // tuple structs act like tuples
En muterbar bindning till en struktur kan ha sina fält muterade:
let mut foo = foo;
foo.my_bool = false;
let mut bar = bar;
bar.0 = false;
Rosts mönster-matchningsmöjligheter kan också användas för att kika in i en struct
:
// creates bindings mb, mn, ms with values of corresponding fields in foo
let Foo { my_bool: mb, my_num: mn, my_string: ms } = foo;
assert_eq!(mn, 42);
// .. allows you to skip fields you do not care about
let Foo { my_num: mn, .. } = foo;
assert_eq!(mn, 42);
// leave out `: variable` to bind a variable by its field name
let Foo { my_num, .. } = foo;
assert_eq!(my_num, 42);
Eller skapa en struktur med en andra struktur som en "mall" med Rusts uppdateringssyntax :
let foo2 = Foo { my_string: String::from("world"), .. foo };
assert_eq!(foo2.my_num, 42);
Strukturmetoder
För att förklara metoder på en struktur (dvs. funktioner som kan kallas "på" struct
eller värdena för den struct
), skapar du ett impl
block:
impl Foo {
fn fiddle(&self) {
// "self" refers to the value this method is being called on
println!("fiddling {}", self.my_string);
}
}
// ...
foo.fiddle(); // prints "fiddling hello"
&self
här indikerar en oföränderlig hänvisning till en instans av struct Foo
är nödvändig för att åberopa fiddle
. Om vi ville ändra förekomsten (som att ändra ett av dess fält), skulle vi istället ta ett &mut self
(dvs. en muterbar referens):
impl Foo {
fn tweak(&mut self, n: isize) {
self.my_num = n;
}
}
// ...
foo.tweak(43);
assert_eq!(foo.my_num, 43);
Slutligen kan vi också använda self
(notera bristen på ett &
) som mottagare. Detta kräver att instansen ägs av den som ringer och kommer att göra att instansen flyttas när man ringer till metoden. Detta kan vara användbart om vi vill konsumera, förstöra eller på annat sätt helt omvandla en befintlig instans. Ett exempel på ett sådant användningsfall är att tillhandahålla "chaining" -metoder:
impl Foo {
fn double(mut self) -> Self {
self.my_num *= 2;
self
}
}
// ...
foo.my_num = 1;
assert_eq!(foo.double().double().my_num, 4);
Observera att vi också förinställde self
med mut
så att vi kan mutera jaget innan vi returnerar det igen. Returtypen för den double
metoden ger också någon förklaring. Self
inuti ett impl
block avser den typ som impl
gäller (i detta fall Foo
). Här är det mestadels en användbar kortfattning för att undvika att skriva om typens signatur, men i drag kan den användas för att hänvisa till den underliggande typen som implementerar en viss egenskap.
Att förklara en tillhörande metod (vanligen kallat ett "klassmetod" på andra språk) för en struct
helt enkelt lämna ut self
argument. Sådana metoder kallas på struct
själv, inte på en instans av den:
impl Foo {
fn new(b: bool, n: isize, s: String) -> Foo {
Foo { my_bool: b, my_num: n, my_string: s }
}
}
// ...
// :: is used to access associated members of the type
let x = Foo::new(false, 0, String::from("nil"));
assert_eq!(x.my_num, 0);
Observera att strukturmetoder endast kan definieras för typer som deklarerades i den aktuella modulen. Liksom med fält är alla struktureringsmetoder som standard privata och kan därför endast kallas med kod i samma modul. Du kan prefixdefinitioner med pub
nyckelord för att göra dem kallabara från någon annanstans.
Generiska strukturer
Strukturer kan göras generiska över en eller flera typparametrar. Dessa typer anges bifogade i <>
när de hänvisar till typen:
struct Gen<T> {
x: T,
z: isize,
}
// ...
let _: Gen<bool> = Gen{x: true, z: 1};
let _: Gen<isize> = Gen{x: 42, z: 2};
let _: Gen<String> = Gen{x: String::from("hello"), z: 3};
Flera typer kan ges med komma:
struct Gen2<T, U> {
x: T,
y: U,
}
// ...
let _: Gen2<bool, isize> = Gen2{x: true, y: 42};
Typparametrarna är en del av typen, så två variabler av samma bastyp, men med olika parametrar, är inte utbytbara:
let mut a: Gen<bool> = Gen{x: true, z: 1};
let b: Gen<isize> = Gen{x: 42, z: 2};
a = b; // this will not work, types are not the same
a.x = 42; // this will not work, the type of .x in a is bool
Om du vill skriva en funktion som accepterar en struct
oberoende av vilken typparametilldelning den skulle, måste den funktionen också göras generisk:
fn hello<T>(g: Gen<T>) {
println!("{}", g.z); // valid, since g.z is always an isize
}
Men tänk om vi ville skriva en funktion som alltid kunde skriva ut gx
? Vi måste begränsa T
till att vara av en typ som kan visas. Vi kan göra detta med typgränser:
use std::fmt;
fn hello<T: fmt::Display>(g: Gen<T>) {
println!("{} {}", g.x, g.z);
}
hello
definieras nu bara för Gen
instanser vars T
typ implementerar fmt::Display
. Om vi till exempel försökte skicka in en Gen<(bool, isize)>
, skulle kompilatorn klaga på att hello
inte är definierat för den typen.
Vi kan också använda typgränser direkt på struct
att indikera att du bara kan konstruera den struct
för vissa typer:
use std::hash::Hash;
struct GenB<T: Hash> {
x: T,
}
Varje funktion som har tillgång till en GenB
vet nu att typen av x
implementerar Hash
och därmed att de kan ringa .x.hash()
. Gränser för flera typer för samma parameter kan ges genom att separera dem med en +
.
Samma som för funktioner, kan typen bounds placeras efter <>
med hjälp av where
nyckelordet:
struct GenB<T> where T: Hash {
x: T,
}
Detta har samma semantiska betydelse, men kan göra signaturen lättare att läsa och formatera när du har komplexa gränser.
Typparametrar är också tillgängliga för instansmetoder och tillhörande metoder för struct
:
// note the <T> parameter for the impl as well
// this is necessary to say that all the following methods only
// exist within the context of those type parameter assignments
impl<T> Gen<T> {
fn inner(self) -> T {
self.x
}
fn new(x: T) -> Gen<T> {
Gen{x: x}
}
}
Om du har typgränser på Gen
's T
, bör dessa också återspeglas i typgränserna för impl
. Du kan också göra impl
hårdare för att säga att en given metod endast finns om typen uppfyller en viss egenskap:
impl<T: Hash + fmt::Display> Gen<T> {
fn show(&self) {
println!("{}", self.x);
}
}
// ...
Gen{x: 42}.show(); // works fine
let a = Gen{x: (42, true)}; // ok, because (isize, bool): Hash
a.show(); // error: (isize, bool) does not implement fmt::Display