Rust Generierter Code Guide
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 vonFoo.
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>: Wieparse, aber schlägt nicht fehl, wennrequired-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>: Wieparse, aber schlägt nicht fehl, wennrequired-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) undrequired-Felder (proto2) nicht gesetzt sind.fn take_from(&mut self, other): Verschiebtothernachselfund verwirft jeden vorherigen Zustand, denselfenthielt.fn copy_from(&mut self, other): Kopiertothernachselfund verwirft jeden vorherigen Zustand, denselfenthielt.otherwird nicht verändert.fn merge_from(&mut self, other): Fügtothernachselfzusammen.fn as_view(&self) -> FooView<'_>: Gibt ein unveränderliches Handle (View) aufFoozurück. Dies wird im Abschnitt über Proxy-Typen weiter behandelt.fn as_mut(&mut self) -> FooMut<'_>: Gibt ein veränderliches Handle (Mut) aufFoozurück. Dies wird im Abschnitt über Proxy-Typen weiter behandelt.
Foo implementiert zusätzlich die folgenden std-Traits
std::fmt::Debugstd::default::Defaultstd::clone::Clonestd::marker::Sendstd::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: Gibttruezurü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 VarianteSet(value)zurück, wenn das Feld gesetzt ist, oderUnset(default value), wenn es nicht gesetzt ist.fn set_foo(&mut self, val: i32): Setzt den Wert des Feldes. Nach dem Aufruf dieser Methode gibthas_foo()truezurück undfoo()gibtvaluezurück.fn clear_foo(&mut self): Löscht den Wert des Feldes. Nach dem Aufruf dieser Methode gibthas_foo()falsezurück undfoo()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: Gibttruezurü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 VarianteSet(value)zurück, wenn das Feld gesetzt ist, oderUnset(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 gibthas_foo()falsezurück undfoo()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: Gibttruezurü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 VarianteSet(value)zurück, wenn das Feld gesetzt ist, oderUnset(default value), wenn es nicht gesetzt ist.fn set_foo(&mut self, val: Bar): Setzt den Wert des Feldes. Nach dem Aufruf dieser Methode gibthas_foo()truezurück undfoo()gibtvaluezurück.fn clear_foo(&mut self): Löscht den Wert des Feldes. Nach dem Aufruf dieser Methode gibthas_foo()falsezurück undfoo()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 gibthas_foo()truezurück.fn foo_opt(&self) -> protobuf::Optional<BarView>: Wenn das Feld gesetzt ist, gibt es die VarianteSetmit seinemvaluezurück. Andernfalls gibt es die VarianteUnsetmit dem Standardwert zurück.fn set_foo(&mut self, value: impl protobuf::IntoProxied<Bar>): Setzt das Feld aufvalue. Nach dem Aufruf dieser Methode gibthas_foo()truezurück.fn has_foo(&self) -> bool: Gibttruezurück, wenn das Feld gesetzt ist.fn clear_foo(&mut self): Löscht das Feld. Nach dem Aufruf dieser Methode gibthas_foo()falsezurü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 es0zurü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 aufvalue.
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 aufvalue. Nach dem Aufruf dieser Funktion gibtfoo()valuezurück undhas_foo()gibttruezurück.fn has_foo(&self) -> bool: Gibttruezurück, wenn das Feld gesetzt ist.fn clear_foo(&mut self): Löscht den Wert des Feldes. Nach dem Aufruf dieser Funktion gibthas_foo()falsezurück undfoo()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 aufvalue.
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 gibthas_foo()truezurück undfoo()gibtvaluezurü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 insrcbereitgestellt 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 aufsrc.
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. Gibtnot_setzurück, wenn kein Feld gesetzt ist.fn example_name_case(&self) -> ExampleNameCase: Gibt die Enum-Variante zurück, die angibt, welches Feld gesetzt ist. Gibtnot_setzurü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.