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


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow