Rust Generierter Code Guide

Beschreibt die API von Nachrichtenobjekten, die der Protocol Buffer-Compiler für jede gegebene Protokolldefinition generiert.

Diese Seite beschreibt genau, welchen Rust-Code der Protokollpuffer-Compiler für jede gegebene Protokolldefinition generiert.

Dieses Dokument beschreibt, wie der Protokollpuffer-Compiler Rust-Code für proto2, proto3 und Protobuf-Editionen generiert. Unterschiede zwischen proto2, proto3 und Editions-generiertem Code werden hervorgehoben. Sie sollten den Proto2 Sprachleitfaden, Proto3 Sprachleitfaden oder Editionsleitfaden lesen, bevor Sie dieses Dokument lesen.

Protobuf Rust

Protobuf Rust ist eine Implementierung von Protokollpuffern, die darauf ausgelegt ist, auf anderen bestehenden Protokollpuffer-Implementierungen aufzubauen, die wir als „Kernel“ bezeichnen.

Die Entscheidung, mehrere Nicht-Rust-Kernel zu unterstützen, hat unsere öffentliche API erheblich beeinflusst, einschließlich der Wahl, benutzerdefinierte Typen wie ProtoStr anstelle von Rust-Standardtypen wie str zu verwenden. Siehe Rust Proto Design Decisions für weitere Informationen zu diesem Thema.

Generierte Dateinamen

Jede rust_proto_library wird als ein Crate kompiliert. Wichtig ist, dass für jede .proto-Datei in den srcs der entsprechenden proto_library eine Rust-Datei ausgegeben wird und all diese Dateien bilden einen einzigen Crate.

Vom Compiler generierte Dateien variieren je nach Kernel. Im Allgemeinen werden die Namen der Ausgabedateien berechnet, indem der Name der .proto-Datei genommen und die Erweiterung ersetzt wird.

Generierte Dateien

  • C++ Kernel
    • .c.pb.rs - Generierter Rust-Code
    • .pb.thunks.cc - Generierte C++ Thunks (Glue-Code, den der Rust-Code aufruft und der an die C++ Protobuf-APIs delegiert).
  • C++ Lite Kernel
    • <Gleich wie C++ Kernel>
  • UPB Kernel
    • .u.pb.rs - Generierter Rust-Code.

Jede proto_library enthält auch eine Datei generated.rs, die als Einstiegspunkt für den Crate behandelt wird. Diese Datei exportiert die Symbole aus allen anderen Rust-Dateien im Crate erneut.

Pakete (Packages)

Im Gegensatz zu den meisten anderen Sprachen werden die package-Deklarationen in den .proto-Dateien im Rust-Codegenerierungsprozess nicht verwendet. Stattdessen gibt jedes rust_proto_library(name = "some_rust_proto")-Ziel einen Crate namens some_rust_proto aus, der den generierten Code für alle .proto-Dateien im Ziel enthält.

Nachrichten

Angesichts der Nachrichtendeklaration

message Foo {}

Der Compiler generiert eine Struktur namens Foo. Die Foo-Struktur definiert die folgenden zugehörigen Funktionen und Methoden

Zugehörige Funktionen

  • fn new() -> Self: Erstellt eine neue Instanz von Foo.

Traits

Aus verschiedenen Gründen, einschließlich der Größe des generierten Codes, Problemen mit Namenskollisionen und der Stabilität des generierten Codes, wird die meiste gängige Funktionalität auf Nachrichten über Traits und nicht über inhärente Implementierungen realisiert.

Die meisten Benutzer sollten unser Prelude importieren, das nur Traits und unser proto!-Makro und keine anderen Typen enthält (use protobuf::prelude::*). Wenn Sie Präfixe lieber vermeiden möchten, können Sie die spezifischen Traits nach Bedarf importieren (siehe die

Dokumentation hier für die Namen und Definitionen der Traits, wenn Sie sie direkt importieren möchten).

  • fn parse(data: &[u8]) -> Result<Self, ParseError>: Parst eine neue Instanz einer Nachricht.
  • fn parse_dont_enforce_required(data: &[u8]) -> Result<Self, ParseError>: Wie parse, aber schlägt nicht fehl, wenn required-Felder von proto2 fehlen.
  • fn clear(&mut self): Löscht die Nachricht.
  • fn clear_and_parse(&mut self, data: &[u8]) -> Result<(), ParseError>: Löscht und parst in eine bestehende Instanz.
  • fn clear_and_parse_dont_enforce_required(&mut self, data: &[u8]) -> Result<(), ParseError>: Wie parse, aber schlägt nicht fehl, wenn required-Felder von proto2 fehlen.
  • fn serialize(&self) -> Result<Vec<u8>, SerializeError>: Serialisiert die Nachricht in das Protobuf Wire-Format. Die Serialisierung kann fehlschlagen, tut dies aber selten. Fehlergründe sind, wenn die Darstellung die maximale kodierte Nachrichtengröße überschreitet (muss kleiner als 2 GiB sein) und required-Felder (proto2) nicht gesetzt sind.
  • fn take_from(&mut self, other): Verschiebt other nach self und verwirft jeden vorherigen Zustand, den self enthielt.
  • fn copy_from(&mut self, other): Kopiert other nach self und verwirft jeden vorherigen Zustand, den self enthielt. other wird nicht verändert.
  • fn merge_from(&mut self, other): Fügt other nach self zusammen.
  • fn as_view(&self) -> FooView<'_>: Gibt ein unveränderliches Handle (View) auf Foo zurück. Dies wird im Abschnitt über Proxy-Typen weiter behandelt.
  • fn as_mut(&mut self) -> FooMut<'_>: Gibt ein veränderliches Handle (Mut) auf Foo zurück. Dies wird im Abschnitt über Proxy-Typen weiter behandelt.

Foo implementiert zusätzlich die folgenden std-Traits

  • std::fmt::Debug
  • std::default::Default
  • std::clone::Clone
  • std::marker::Send
  • std::marker::Sync

Instanzen flüssig erstellen

Das API-Design von Settern folgt unseren etablierten Protobuf-Idiomen, aber die Ausführlichkeit bei der Erstellung neuer Instanzen ist in bestimmten anderen Sprachen ein leichtes Problem. Um dies zu mildern, bieten wir ein proto!-Makro an, mit dem neue Instanzen prägnanter/flüssiger erstellt werden können.

Anstatt beispielsweise dies zu schreiben

let mut msg = SomeMsg::new();
msg.set_x(1);
msg.set_y("hello");
msg.some_submessage_mut().set_z(42);

Dieses Makro kann verwendet werden, um es wie folgt zu schreiben

let msg = proto!(SomeMsg {
  x: 1,
  y: "hello",
  some_submsg: SomeSubmsg {
    z: 42
  }
});

Nachricht-Proxy-Typen

Aus verschiedenen technischen Gründen haben wir uns entschieden, native Rust-Referenzen (&T und &mut T) in bestimmten Fällen zu vermeiden. Stattdessen müssen wir diese Konzepte mit Typen ausdrücken - Views und Muts. Diese Situationen sind gemeinsam und veränderliche Referenzen zu

  • Nachrichten
  • Wiederholte Felder
  • Map-Felder

Zum Beispiel gibt der Compiler die Strukturen FooView<'a> und FooMut<'msg> neben Foo aus. Diese Typen werden anstelle von &Foo und &mut Foo verwendet und verhalten sich in Bezug auf das Borrow-Checker-Verhalten genauso wie native Rust-Referenzen. Genau wie native Borrows sind Views Copy und der Borrow-Checker stellt sicher, dass Sie entweder beliebig viele Views oder höchstens eine Mut-Instanz gleichzeitig haben können.

Für die Zwecke dieser Dokumentation konzentrieren wir uns auf die Beschreibung aller Methoden, die für den besessenen Nachrichtentyp (Foo) ausgegeben werden. Eine Teilmenge dieser Funktionen mit &self-Empfänger wird auch auf FooView<'msg> enthalten sein. Eine Teilmenge dieser Funktionen mit entweder &self oder &mut self wird auch auf FooMut<'msg> enthalten sein.

Um einen besessenen Nachrichtentyp aus einem View/Mut-Typ zu erstellen, rufen Sie to_owned() auf, was eine tiefe Kopie erstellt.

Siehe den entsprechenden Abschnitt in unserer Designentscheidungsdokumentation für weitere Diskussionen darüber, warum diese Wahl getroffen wurde.

Verschachtelte Typen

Angesichts der Nachrichtendeklaration

message Foo {
  message Bar {
      enum Baz { ... }
  }
}

Zusätzlich zur Struktur namens Foo wird ein Modul namens foo erstellt, um die Struktur für Bar zu enthalten. Und ähnlich ein verschachteltes Modul namens bar, um das tief verschachtelte Enum Baz zu enthalten.

pub struct Foo {}

pub mod foo {
   pub struct Bar {}
   pub mod bar {
      pub struct Baz { ... }
   }
}

Felder

Zusätzlich zu den in der vorherigen Sektion beschriebenen Methoden generiert der Protokollpuffer-Compiler eine Reihe von Accessor-Methoden für jedes Feld, das innerhalb der Nachricht in der .proto-Datei definiert ist.

Gemäß dem Rust-Stil sind die Methoden in Kleinbuchstaben/Snake-Case geschrieben, wie z. B. has_foo() und clear_foo(). Beachten Sie, dass die Groß-/Kleinschreibung des Feldnamen-Teils des Accessors den Stil der ursprünglichen .proto-Datei beibehält, die wiederum gemäß dem .proto-Dateistilhandbuch Kleinbuchstaben/Snake-Case sein sollte.

Felder mit expliziter Präsenz

Explizite Präsenz bedeutet, dass ein Feld zwischen dem Standardwert und keinem gesetzten Wert unterscheidet. In proto2 haben optional-Felder explizite Präsenz. In proto3 haben nur Nachrichtenfelder und oneof- oder optional-Felder explizite Präsenz. Die Präsenz wird mit der features.field_presence-Option in Editionen gesetzt.

Numerische Felder

Für diese Felddefinition:

int32 foo = 1;

Der Compiler generiert die folgenden Accessor-Methoden

  • fn has_foo(&self) -> bool: Gibt true zurück, wenn das Feld gesetzt ist.
  • fn foo(&self) -> i32: Gibt den aktuellen Wert des Feldes zurück. Wenn das Feld nicht gesetzt ist, gibt es den Standardwert zurück.
  • fn foo_opt(&self) -> protobuf::Optional<i32>: Gibt ein Optional mit der Variante Set(value) zurück, wenn das Feld gesetzt ist, oder Unset(default value), wenn es nicht gesetzt ist.
  • fn set_foo(&mut self, val: i32): Setzt den Wert des Feldes. Nach dem Aufruf dieser Methode gibt has_foo() true zurück und foo() gibt value zurück.
  • fn clear_foo(&mut self): Löscht den Wert des Feldes. Nach dem Aufruf dieser Methode gibt has_foo() false zurück und foo() gibt den Standardwert zurück.

Für andere numerische Feldtypen (einschließlich bool) wird int32 gemäß der Tabelle der Skalarwerttypen durch den entsprechenden Rust-Typ ersetzt.

String- und Bytes-Felder

Für diese Felddefinitionen

string foo = 1;
bytes foo = 1;

Der Compiler generiert die folgenden Accessor-Methoden

  • fn has_foo(&self) -> bool: Gibt true zurück, wenn das Feld gesetzt ist.
  • fn foo(&self) -> &protobuf::ProtoStr: Gibt den aktuellen Wert des Feldes zurück. Wenn das Feld nicht gesetzt ist, gibt es den Standardwert zurück.
  • fn foo_opt(&self) -> protobuf::Optional<&ProtoStr>: Gibt ein Optional mit der Variante Set(value) zurück, wenn das Feld gesetzt ist, oder Unset(default value), wenn es nicht gesetzt ist.
  • fn set_foo(&mut self, val: impl IntoProxied<ProtoString>): Setzt den Wert des Feldes.
  • fn clear_foo(&mut self): Löscht den Wert des Feldes. Nach dem Aufruf dieser Methode gibt has_foo() false zurück und foo() gibt den Standardwert zurück.

Für Felder vom Typ bytes generiert der Compiler stattdessen den Typ ProtoBytes.

Enum-Felder

Angesichts dieser Enum-Definition in jeder Proto-Syntaxversion

enum Bar {
  BAR_UNSPECIFIED = 0;
  BAR_VALUE = 1;
  BAR_OTHER_VALUE = 2;
}

Der Compiler generiert eine Struktur, in der jede Variante eine zugehörige Konstante ist.

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Bar(i32);

impl Bar {
  pub const Unspecified: Bar = Bar(0);
  pub const Value: Bar = Bar(1);
  pub const OtherValue: Bar = Bar(2);
}

Für diese Felddefinition:

Bar foo = 1;

Der Compiler generiert die folgenden Accessor-Methoden

  • fn has_foo(&self) -> bool: Gibt true zurück, wenn das Feld gesetzt ist.
  • fn foo(&self) -> Bar: Gibt den aktuellen Wert des Feldes zurück. Wenn das Feld nicht gesetzt ist, gibt es den Standardwert zurück.
  • fn foo_opt(&self) -> Optional<Bar>: Gibt ein Optional mit der Variante Set(value) zurück, wenn das Feld gesetzt ist, oder Unset(default value), wenn es nicht gesetzt ist.
  • fn set_foo(&mut self, val: Bar): Setzt den Wert des Feldes. Nach dem Aufruf dieser Methode gibt has_foo() true zurück und foo() gibt value zurück.
  • fn clear_foo(&mut self): Löscht den Wert des Feldes. Nach dem Aufruf dieser Methode gibt has_foo() false zurück und foo() gibt den Standardwert zurück.

Eingebettete Nachrichtenfelder

Angesichts des Nachrichtentyps Bar aus jeder Proto-Syntaxversion

message Bar {}

Für jede dieser Felddefinitionen


message MyMessage {
  Bar foo = 1;
}

Der Compiler generiert die folgenden Accessor-Methoden:

  • fn foo(&self) -> BarView<'_>: Gibt eine Ansicht des aktuellen Feldwertes zurück. Wenn das Feld nicht gesetzt ist, wird eine leere Nachricht zurückgegeben.
  • fn foo_mut(&mut self) -> BarMut<'_>: Gibt ein veränderliches Handle für den aktuellen Feldwert zurück. Setzt das Feld, wenn es nicht gesetzt ist. Nach dem Aufruf dieser Methode gibt has_foo() true zurück.
  • fn foo_opt(&self) -> protobuf::Optional<BarView>: Wenn das Feld gesetzt ist, gibt es die Variante Set mit seinem value zurück. Andernfalls gibt es die Variante Unset mit dem Standardwert zurück.
  • fn set_foo(&mut self, value: impl protobuf::IntoProxied<Bar>): Setzt das Feld auf value. Nach dem Aufruf dieser Methode gibt has_foo() true zurück.
  • fn has_foo(&self) -> bool: Gibt true zurück, wenn das Feld gesetzt ist.
  • fn clear_foo(&mut self): Löscht das Feld. Nach dem Aufruf dieser Methode gibt has_foo() false zurück.

Felder mit impliziter Präsenz (proto3 und Editionen)

Implizite Präsenz bedeutet, dass ein Feld nicht zwischen dem Standardwert und keinem gesetzten Wert unterscheidet. In proto3 haben Felder standardmäßig implizite Präsenz. In Editionen können Sie ein Feld mit impliziter Präsenz deklarieren, indem Sie das field_presence-Feature auf IMPLICIT setzen.

Numerische Felder

Für diese Felddefinitionen

// proto3
int32 foo = 1;

// editions
message MyMessage {
  int32 foo = 1 [features.field_presence = IMPLICIT];
}

Der Compiler generiert die folgenden Accessor-Methoden

  • fn foo(&self) -> i32: Gibt den aktuellen Wert des Feldes zurück. Wenn das Feld nicht gesetzt ist, gibt es 0 zurück.
  • fn set_foo(&mut self, val: i32): Setzt den Wert des Feldes.

Für andere numerische Feldtypen (einschließlich bool) wird int32 gemäß der Tabelle der Skalarwerttypen durch den entsprechenden Rust-Typ ersetzt.

String- und Bytes-Felder

Für diese Felddefinitionen

// proto3
string foo = 1;
bytes foo = 1;

// editions
string foo = 1 [features.field_presence = IMPLICIT];
bytes bar = 2 [features.field_presence = IMPLICIT];

Der Compiler generiert die folgenden Accessor-Methoden:

  • fn foo(&self) -> &ProtoStr: Gibt den aktuellen Wert des Feldes zurück. Wenn das Feld nicht gesetzt ist, wird der leere String/die leeren Bytes zurückgegeben.
  • fn set_foo(&mut self, value: IntoProxied<ProtoString>): Setzt das Feld auf value.

Für Felder vom Typ bytes generiert der Compiler stattdessen den Typ ProtoBytes.

Singular String- und Bytes-Felder mit Cord-Unterstützung

[ctype = CORD] ermöglicht es, Bytes und Strings als absl::Cord in C++ Protobufs zu speichern. absl::Cord hat derzeit keinen gleichwertigen Typ in Rust. Protobuf Rust verwendet ein Enum, um ein Cord-Feld darzustellen.

enum ProtoStringCow<'a> {
  Owned(ProtoString),
  Borrowed(&'a ProtoStr)
}

Im gängigen Fall, bei kleinen Strings, speichert ein absl::Cord seine Daten als zusammenhängenden String. In diesem Fall geben Cord-Accessoren ProtoStringCow::Borrowed zurück. Wenn der zugrunde liegende absl::Cord nicht zusammenhängend ist, kopiert der Accessor die Daten aus dem Cord in ein besessenes ProtoString und gibt ProtoStringCow::Owned zurück. Die ProtoStringCow implementiert Deref<Target=ProtoStr>.

Für jede dieser Felddefinitionen

optional string foo = 1 [ctype = CORD];
string foo = 1 [ctype = CORD];
optional bytes foo = 1 [ctype = CORD];
bytes foo = 1 [ctype = CORD];

Der Compiler generiert die folgenden Accessor-Methoden

  • fn my_field(&self) -> ProtoStringCow<'_>: Gibt den aktuellen Wert des Feldes zurück. Wenn das Feld nicht gesetzt ist, wird der leere String/die leeren Bytes zurückgegeben.
  • fn set_my_field(&mut self, value: IntoProxied<ProtoString>): Setzt das Feld auf value. Nach dem Aufruf dieser Funktion gibt foo() value zurück und has_foo() gibt true zurück.
  • fn has_foo(&self) -> bool: Gibt true zurück, wenn das Feld gesetzt ist.
  • fn clear_foo(&mut self): Löscht den Wert des Feldes. Nach dem Aufruf dieser Funktion gibt has_foo() false zurück und foo() gibt den Standardwert zurück. Cords wurden noch nicht implementiert.

Für Felder vom Typ bytes generiert der Compiler stattdessen den Typ ProtoBytesCow.

Der Compiler generiert die folgenden Accessor-Methoden

  • fn foo(&self) -> &ProtoStr: Gibt den aktuellen Wert des Feldes zurück. Wenn das Feld nicht gesetzt ist, wird der leere String/die leeren Bytes zurückgegeben.
  • fn set_foo(&mut self, value: impl IntoProxied<ProtoString>): Setzt das Feld auf value.

Enum-Felder

Angesichts des Enum-Typs

enum Bar {
  BAR_UNSPECIFIED = 0;
  BAR_VALUE = 1;
  BAR_OTHER_VALUE = 2;
}

Der Compiler generiert eine Struktur, in der jede Variante eine zugehörige Konstante ist.

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Bar(i32);

impl Bar {
  pub const Unspecified: Bar = Bar(0);
  pub const Value: Bar = Bar(1);
  pub const OtherValue: Bar = Bar(2);
}

Für diese Felddefinitionen

// proto3
Bar foo = 1;

// editions
message MyMessage {
 Bar foo = 1 [features.field_presence = IMPLICIT];
}

Der Compiler generiert die folgenden Accessor-Methoden:

  • fn foo(&self) -> Bar: Gibt den aktuellen Wert des Feldes zurück. Wenn das Feld nicht gesetzt ist, gibt es den Standardwert zurück.
  • fn set_foo(&mut self, value: Bar): Setzt den Wert des Feldes. Nach dem Aufruf dieser Methode gibt has_foo() true zurück und foo() gibt value zurück.

Wiederholte Felder

Für jedes wiederholte Feld generiert der Compiler dieselben drei Accessor-Methoden, die sich nur im Feldtyp unterscheiden.

In Editionen können Sie die Wire-Format-Kodierung von wiederholten primitiven Feldern über das Feature repeated_field_encoding steuern.

// proto2
repeated int32 foo = 1; // EXPANDED by default

// proto3
repeated int32 foo = 1; // PACKED by default

// editions
repeated int32 foo = 1 [features.repeated_field_encoding = PACKED];
repeated int32 bar = 2 [features.repeated_field_encoding = EXPANDED];

Angesichts einer der oben genannten Felddefinitionen generiert der Compiler die folgenden Accessor-Methoden

  • fn foo(&self) -> RepeatedView<'_, i32>: Gibt eine Ansicht des zugrunde liegenden wiederholten Feldes zurück.
  • fn foo_mut(&mut self) -> RepeatedMut<'_, i32>: Gibt ein veränderliches Handle für das zugrunde liegende wiederholte Feld zurück.
  • fn set_foo(&mut self, src: impl IntoProxied<Repeated<i32>>): Setzt das zugrunde liegende wiederholte Feld auf ein neues wiederholtes Feld, das in src bereitgestellt wird.

Für verschiedene Feldtypen ändern sich nur die jeweiligen generischen Typen der RepeatedView-, RepeatedMut- und Repeated-Typen. Zum Beispiel würde bei einem Feld vom Typ string der foo()-Accessor eine RepeatedView<'_, ProtoString> zurückgeben.

Map-Felder

Für diese Map-Felddefinition

map<int32, int32> weight = 1;

Der Compiler generiert die folgenden 3 Accessor-Methoden

  • fn weight(&self) -> protobuf::MapView<'_, i32, i32>: Gibt eine unveränderliche Ansicht der zugrunde liegenden Map zurück.
  • fn weight_mut(&mut self) -> protobuf::MapMut<'_, i32, i32>: Gibt ein veränderliches Handle für die zugrunde liegende Map zurück.
  • fn set_weight(&mut self, src: protobuf::IntoProxied<Map<i32, i32>>): Setzt die zugrunde liegende Map auf src.

Für verschiedene Feldtypen ändern sich nur die jeweiligen generischen Typen der MapView-, MapMut- und Map-Typen. Zum Beispiel würde bei einem Feld vom Typ string der foo()-Accessor eine MapView<'_, int32, ProtoString> zurückgeben.

Any

any wird derzeit von Rust Protobuf nicht speziell behandelt; es wird so behandelt, als wäre es eine einfache Nachricht mit dieser Definition.

message Any {
  string type_url = 1;
  bytes value = 2;
}

Oneof

Angesichts einer Oneof-Definition wie dieser

oneof example_name {
    int32 foo_int = 4;
    string foo_string = 9;
    ...
}

Der Compiler generiert Accessoren (Getter, Setter, Hazzer) für jedes Feld, als ob dasselbe Feld als optional-Feld außerhalb des Oneof deklariert worden wäre. Sie können also mit Oneof-Feldern wie normale Felder arbeiten, aber das Setzen eines Feldes löscht die anderen Felder im Oneof-Block. Zusätzlich werden die folgenden Typen für den oneof-Block ausgegeben.

  #[non_exhaustive]
  #[derive(Debug, Clone, Copy)]

  pub enum ExampleNameOneof<'msg> {
    FooInt(i32) = 4,
    FooString(&'msg protobuf::ProtoStr) = 9,
    not_set(std::marker::PhantomData<&'msg ()>) = 0
  }
  #[derive(Debug, Copy, Clone, PartialEq, Eq)]

  pub enum ExampleNameCase {
    FooInt = 4,
    FooString = 9,
    not_set = 0
  }

Zusätzlich generiert er die beiden Accessoren

  • fn example_name(&self) -> ExampleNameOneof<_>: Gibt die Enum-Variante zurück, die angibt, welches Feld gesetzt ist und welchen Wert das Feld hat. Gibt not_set zurück, wenn kein Feld gesetzt ist.
  • fn example_name_case(&self) -> ExampleNameCase: Gibt die Enum-Variante zurück, die angibt, welches Feld gesetzt ist. Gibt not_set zurück, wenn kein Feld gesetzt ist.

Aufzählungen (Enums)

Gegeben eine Enum-Definition wie

enum FooBar {
  FOO_BAR_UNKNOWN = 0;
  FOO_BAR_A = 1;
  FOO_B = 5;
  VALUE_C = 1234;
}

Der Compiler generiert

  #[derive(Clone, Copy, PartialEq, Eq, Hash)]
  #[repr(transparent)]
  pub struct FooBar(i32);

  impl FooBar {
    pub const Unknown: FooBar = FooBar(0);
    pub const A: FooBar = FooBar(1);
    pub const FooB: FooBar = FooBar(5);
    pub const ValueC: FooBar = FooBar(1234);
  }

Beachten Sie, dass für Werte mit einem Präfix, das mit dem Enum übereinstimmt, das Präfix entfernt wird; dies geschieht zur Verbesserung der Ergonomie. Enum-Werte werden häufig mit dem Enum-Namen präfixiert, um Namenskollisionen zwischen Geschwister-Enums zu vermeiden (die den Semantiken von C++-Enums folgen, bei denen die Werte nicht durch ihr enthaltendes Enum beschränkt sind). Da die generierten Rust-Konstanten innerhalb der impl-Blöcke gekapselt sind, wäre das zusätzliche Präfix, das in .proto-Dateien hilfreich ist, in Rust redundant.

Erweiterungen (nur proto2)

Eine Rust-API für Erweiterungen ist derzeit in Arbeit. Erweiterungsfelder werden über Parse/Serialize beibehalten, und in einem C++-Interoperabilität-Fall werden alle gesetzten Erweiterungen beibehalten, wenn die Nachricht aus Rust zugegriffen wird (und im Falle einer Nachrichtenkopie oder -zusammenführung weitergegeben).

Arena-Allokation

Eine Rust-API für Arena-alloziierte Nachrichten wurde noch nicht implementiert.

Intern verwendet Protobuf Rust auf dem UPB-Kernel Arenen, aber auf C++-Kerneln nicht. Referenzen (sowohl const als auch mutable) zu Nachrichten, die in C++ Arena-alloziert waren, können jedoch sicher an Rust übergeben werden, um dort zugegriffen oder geändert zu werden.

Services

Eine Rust-API für Services wurde noch nicht implementiert.