// Protocol Buffers - Google's data interchange format // Copyright 2023 Google LLC. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd //! Operating on borrowed data owned by a message is a central concept in //! Protobuf (and Rust in general). The way this is normally accomplished in //! Rust is to pass around references and operate on those. Unfortunately, //! references come with two major drawbacks: //! //! * We must store the value somewhere in the memory to create a reference to //! it. The value must be readable by a single load. However for Protobuf //! fields it happens that the actual memory representation of a value differs //! from what users expect and it is an implementation detail that can change //! as more optimizations are implemented. For example, rarely accessed //! `int64` fields can be represented in a packed format with 32 bits for the //! value in the common case. Or, a single logical value can be spread across //! multiple memory locations. For example, presence information for all the //! fields in a protobuf message is centralized in a bitset. //! * We cannot store extra data on the reference that might be necessary for //! correctly manipulating it (and custom-metadata DSTs do not exist yet in //! Rust). Concretely, messages, string, bytes, and repeated fields in UPB //! need to carry around an arena parameter separate from the data pointer to //! enable mutation (for example adding an element to a repeated field) or //! potentially to enable optimizations (for example referencing a string //! value using a Cord-like type instead of copying it if the source and //! target messages are on the same arena already). Mutable references to //! messages have one additional drawback: Rust allows users to //! indiscriminately run a bytewise swap() on mutable references, which could //! result in pointers to the wrong arena winding up on a message. For //! example, imagine swapping a submessage across two root messages allocated //! on distinct arenas A and B; after the swap, the message allocated in A may //! contain pointers from B by way of the submessage, because the swap does //! not know to fix up those pointers as needed. The C++ API uses //! message-owned arenas, and this ends up resembling self-referential types, //! which need `Pin` in order to be sound. However, `Pin` has much stronger //! guarantees than we need to uphold. //! //! These drawbacks put the "idiomatic Rust" goal in conflict with the //! "performance", "evolvability", and "safety" goals. Given the project design //! priorities we decided to not use plain Rust references. Instead, we //! implemented the concept of "proxy" types. Proxy types are a reference-like //! indirection between the user and the internal memory representation. use crate::__internal::{Private, SealedInternal}; use std::fmt::Debug; /// A type that can be accessed through a reference-like proxy. /// /// An instance of a `Proxied` can be accessed immutably via `Proxied::View`. /// /// All Protobuf field types implement `Proxied`. pub trait Proxied: SealedInternal + AsView + Sized { /// The proxy type that provides shared access to a `T`, like a `&'msg T`. /// /// Most code should use the type alias [`View`]. type View<'msg>: ViewProxy<'msg, Proxied = Self> where Self: 'msg; } /// A type that can be be accessed through a reference-like proxy. /// /// An instance of a `MutProxied` can be accessed mutably via `MutProxied::Mut` /// and immutably via `MutProxied::View`. /// /// `MutProxied` is implemented by message, map and repeated field types. pub trait MutProxied: SealedInternal + Proxied + AsMut { /// The proxy type that provides exclusive mutable access to a `T`, like a /// `&'msg mut T`. /// /// Most code should use the type alias [`Mut`]. type Mut<'msg>: MutProxy<'msg, MutProxied = Self> where Self: 'msg; } /// A proxy type that provides shared access to a `T`, like a `&'msg T`. /// /// This is more concise than fully spelling the associated type. #[allow(dead_code)] pub type View<'msg, T> = ::View<'msg>; /// A proxy type that provides exclusive mutable access to a `T`, like a /// `&'msg mut T`. /// /// This is more concise than fully spelling the associated type. #[allow(dead_code)] pub type Mut<'msg, T> = ::Mut<'msg>; /// Used to semantically do a cheap "to-reference" conversion. This is /// implemented on both owned `Proxied` types as well as ViewProxy and MutProxy /// types. /// /// On ViewProxy this will behave as a reborrow into a shorter lifetime. pub trait AsView: SealedInternal { type Proxied: Proxied; /// Converts a borrow into a `View` with the lifetime of that borrow. /// /// In non-generic code we don't need to use `as_view` because the proxy /// types are covariant over `'msg`. However, generic code conservatively /// treats `'msg` as [invariant], therefore we need to call /// `as_view` to explicitly perform the operation that in concrete code /// coercion would perform implicitly. /// /// For example, the call to `.as_view()` in the following snippet /// wouldn't be necessary in concrete code: /// ```ignore /// fn reborrow<'a, 'b, T>(x: &'b View<'a, T>) -> View<'b, T> /// where 'a: 'b, T: Proxied /// { /// x.as_view() /// } /// ``` /// /// [invariant]: https://doc.rust-lang.org/nomicon/subtyping.html#variance fn as_view(&self) -> View<'_, Self::Proxied>; } /// Used to turn another 'borrow' into a ViewProxy. /// /// On a MutProxy this borrows to a View (semantically matching turning a `&mut /// T` into a `&T`). /// /// On a ViewProxy this will behave as a reborrow into a shorter lifetime /// (semantically matching a `&'a T` into a `&'b T` where `'a: 'b`). pub trait IntoView<'msg>: SealedInternal + AsView { /// Converts into a `View` with a potentially shorter lifetime. /// /// In non-generic code we don't need to use `into_view` because the proxy /// types are covariant over `'msg`. However, generic code conservatively /// treats `'msg` as [invariant], therefore we need to call /// `into_view` to explicitly perform the operation that in concrete /// code coercion would perform implicitly. /// /// ```ignore /// fn reborrow_generic_view_into_view<'a, 'b, T>( /// x: View<'a, T>, /// y: View<'b, T>, /// ) -> [View<'b, T>; 2] /// where /// T: MutProxied, /// 'a: 'b, /// { /// // `[x, y]` fails to compile because `'a` is not the same as `'b` and the `View` /// // lifetime parameter is (conservatively) invariant. /// // `[x.as_view(), y]` fails because that borrow cannot outlive `'b`. /// [x.into_view(), y] /// } /// ``` /// /// [invariant]: https://doc.rust-lang.org/nomicon/subtyping.html#variance fn into_view<'shorter>(self) -> View<'shorter, Self::Proxied> where 'msg: 'shorter; } /// Used to semantically do a cheap "to-mut-reference" conversion. This is /// implemented on both owned `Proxied` types as well as MutProxy types. /// /// On MutProxy this will behave as a reborrow into a shorter lifetime. pub trait AsMut: SealedInternal + AsView { type MutProxied: MutProxied; /// Converts a borrow into a `Mut` with the lifetime of that borrow. fn as_mut(&mut self) -> Mut<'_, Self::MutProxied>; } /// Used to turn another 'borrow' into a MutProxy. /// /// On a MutProxy this will behave as a reborrow into a shorter lifetime /// (semantically matching a `&mut 'a T` into a `&mut 'b T` where `'a: 'b`). pub trait IntoMut<'msg>: SealedInternal + AsMut { /// Converts into a `Mut` with a potentially shorter lifetime. /// /// In non-generic code we don't need to use `into_mut` because the proxy /// types are covariant over `'msg`. However, generic code conservatively /// treats `'msg` as [invariant], therefore we need to call /// `into_mut` to explicitly perform the operation that in concrete code /// coercion would perform implicitly. /// /// ```ignore /// fn reborrow_generic_mut_into_mut<'a, 'b, T>(x: Mut<'a, T>, y: Mut<'b, T>) -> [Mut<'b, T>; 2] /// where /// T: Proxied, /// 'a: 'b, /// { /// // `[x, y]` fails to compile because `'a` is not the same as `'b` and the `Mut` /// // lifetime parameter is (conservatively) invariant. /// // `[x.as_mut(), y]` fails because that borrow cannot outlive `'b`. /// [x.into_mut(), y] /// } /// ``` /// /// [invariant]: https://doc.rust-lang.org/nomicon/subtyping.html#variance fn into_mut<'shorter>(self) -> Mut<'shorter, Self::MutProxied> where 'msg: 'shorter; } /// Declares conversion operations common to all proxies (both views and mut /// proxies). /// /// This trait is intentionally made non-object-safe to prevent a potential /// future incompatible change. pub trait Proxy<'msg>: SealedInternal + 'msg + IntoView<'msg> + Sync + Unpin + Sized + Debug { } /// Declares conversion operations common to view proxies. pub trait ViewProxy<'msg>: SealedInternal + Proxy<'msg> + Send {} /// Declares operations common to all mut proxies. /// /// This trait is intentionally made non-object-safe to prevent a potential /// future incompatible change. pub trait MutProxy<'msg>: SealedInternal + Proxy<'msg> + AsMut + IntoMut<'msg> { /// Gets an immutable view of this field. This is shorthand for `as_view`. /// /// This provides a shorter lifetime than `into_view` but can also be called /// multiple times - if the result of `get` is not living long enough /// for your use, use that instead. fn get(&self) -> View<'_, Self::Proxied> { self.as_view() } } /// A value to `Proxied`-value conversion that consumes the input value. /// /// All setter functions accept types that implement `IntoProxied`. The purpose /// of `IntoProxied` is to allow setting arbitrary values on Protobuf fields /// with the minimal number of copies. /// /// This trait must not be implemented on types outside the Protobuf codegen and /// runtime. We expect it to change in backwards incompatible ways in the /// future. pub trait IntoProxied { #[doc(hidden)] fn into_proxied(self, _private: Private) -> T; } impl IntoProxied for T { fn into_proxied(self, _private: Private) -> T { self } } #[cfg(test)] mod tests { use super::*; use googletest::prelude::*; #[derive(Debug, Default, PartialEq)] struct MyProxied { val: String, } impl MyProxied { fn as_view(&self) -> View<'_, Self> { MyProxiedView { my_proxied_ref: self } } fn as_mut(&mut self) -> Mut<'_, Self> { MyProxiedMut { my_proxied_ref: self } } } impl SealedInternal for MyProxied {} impl Proxied for MyProxied { type View<'msg> = MyProxiedView<'msg>; } impl AsView for MyProxied { type Proxied = Self; fn as_view(&self) -> MyProxiedView<'_> { self.as_view() } } impl MutProxied for MyProxied { type Mut<'msg> = MyProxiedMut<'msg>; } impl AsMut for MyProxied { type MutProxied = Self; fn as_mut(&mut self) -> MyProxiedMut<'_> { self.as_mut() } } #[derive(Debug, Clone, Copy)] struct MyProxiedView<'msg> { my_proxied_ref: &'msg MyProxied, } impl<'msg> SealedInternal for MyProxiedView<'msg> {} impl MyProxiedView<'_> { fn val(&self) -> &str { &self.my_proxied_ref.val } } impl<'msg> Proxy<'msg> for MyProxiedView<'msg> {} impl<'msg> ViewProxy<'msg> for MyProxiedView<'msg> {} impl<'msg> AsView for MyProxiedView<'msg> { type Proxied = MyProxied; fn as_view(&self) -> MyProxiedView<'msg> { *self } } impl<'msg> IntoView<'msg> for MyProxiedView<'msg> { fn into_view<'shorter>(self) -> MyProxiedView<'shorter> where 'msg: 'shorter, { self } } #[derive(Debug)] struct MyProxiedMut<'msg> { my_proxied_ref: &'msg mut MyProxied, } impl<'msg> SealedInternal for MyProxiedMut<'msg> {} impl<'msg> Proxy<'msg> for MyProxiedMut<'msg> {} impl<'msg> AsView for MyProxiedMut<'msg> { type Proxied = MyProxied; fn as_view(&self) -> MyProxiedView<'_> { MyProxiedView { my_proxied_ref: self.my_proxied_ref } } } impl<'msg> IntoView<'msg> for MyProxiedMut<'msg> { fn into_view<'shorter>(self) -> View<'shorter, MyProxied> where 'msg: 'shorter, { MyProxiedView { my_proxied_ref: self.my_proxied_ref } } } impl<'msg> AsMut for MyProxiedMut<'msg> { type MutProxied = MyProxied; fn as_mut(&mut self) -> MyProxiedMut<'_> { MyProxiedMut { my_proxied_ref: self.my_proxied_ref } } } impl<'msg> IntoMut<'msg> for MyProxiedMut<'msg> { fn into_mut<'shorter>(self) -> MyProxiedMut<'shorter> where 'msg: 'shorter, { self } } impl<'msg> MutProxy<'msg> for MyProxiedMut<'msg> {} #[gtest] fn test_as_view() { let my_proxied = MyProxied { val: "Hello World".to_string() }; let my_view = my_proxied.as_view(); assert_that!(my_view.val(), eq(&my_proxied.val)); } fn reborrow_mut_into_view<'msg>(x: Mut<'msg, MyProxied>) -> View<'msg, MyProxied> { // x.as_view() fails to compile with: // `ERROR: attempt to return function-local borrowed content` x.into_view() // OK: we return the same lifetime as we got in. } #[gtest] fn test_mut_into_view() { let mut my_proxied = MyProxied { val: "Hello World".to_string() }; reborrow_mut_into_view(my_proxied.as_mut()); } fn require_unified_lifetimes<'msg>(_x: Mut<'msg, MyProxied>, _y: View<'msg, MyProxied>) {} #[gtest] fn test_require_unified_lifetimes() { let mut my_proxied = MyProxied { val: "Hello1".to_string() }; let my_mut = my_proxied.as_mut(); { let other_proxied = MyProxied { val: "Hello2".to_string() }; let other_view = other_proxied.as_view(); require_unified_lifetimes(my_mut, other_view); } } fn reborrow_generic_as_view<'a, 'b, T>( x: &'b mut Mut<'a, T>, y: &'b View<'a, T>, ) -> [View<'b, T>; 2] where T: MutProxied, 'a: 'b, { // `[x, y]` fails to compile because `'a` is not the same as `'b` and the `View` // lifetime parameter is (conservatively) invariant. [x.as_view(), y.as_view()] } #[gtest] fn test_reborrow_generic_as_view() { let mut my_proxied = MyProxied { val: "Hello1".to_string() }; let mut my_mut = my_proxied.as_mut(); let my_ref = &mut my_mut; { let other_proxied = MyProxied { val: "Hello2".to_string() }; let other_view = other_proxied.as_view(); reborrow_generic_as_view::(my_ref, &other_view); } } fn reborrow_generic_view_into_view<'a, 'b, T>( x: View<'a, T>, y: View<'b, T>, ) -> [View<'b, T>; 2] where T: Proxied, 'a: 'b, { // `[x, y]` fails to compile because `'a` is not the same as `'b` and the `View` // lifetime parameter is (conservatively) invariant. // `[x.as_view(), y]` fails because that borrow cannot outlive `'b`. [x.into_view(), y] } #[gtest] fn test_reborrow_generic_into_view() { let my_proxied = MyProxied { val: "Hello1".to_string() }; let my_view = my_proxied.as_view(); { let other_proxied = MyProxied { val: "Hello2".to_string() }; let other_view = other_proxied.as_view(); reborrow_generic_view_into_view::(my_view, other_view); } } fn reborrow_generic_mut_into_view<'a, 'b, T>(x: Mut<'a, T>, y: View<'b, T>) -> [View<'b, T>; 2] where T: MutProxied, 'a: 'b, { [x.into_view(), y] } #[gtest] fn test_reborrow_generic_mut_into_view() { let mut my_proxied = MyProxied { val: "Hello1".to_string() }; let my_mut = my_proxied.as_mut(); { let other_proxied = MyProxied { val: "Hello2".to_string() }; let other_view = other_proxied.as_view(); reborrow_generic_mut_into_view::(my_mut, other_view); } } fn reborrow_generic_mut_into_mut<'a, 'b, T>(x: Mut<'a, T>, y: Mut<'b, T>) -> [Mut<'b, T>; 2] where T: MutProxied, 'a: 'b, { // `[x, y]` fails to compile because `'a` is not the same as `'b` and the `Mut` // lifetime parameter is (conservatively) invariant. // `[x.as_mut(), y]` fails because that borrow cannot outlive `'b`. let tmp: Mut<'b, T> = x.into_mut(); [tmp, y] } #[gtest] fn test_reborrow_generic_mut_into_mut() { let mut my_proxied = MyProxied { val: "Hello1".to_string() }; let my_mut = my_proxied.as_mut(); { let mut other_proxied = MyProxied { val: "Hello2".to_string() }; let other_mut = other_proxied.as_mut(); // No need to reborrow, even though lifetime of &other_view is different // than the lifetiem of my_ref. Rust references are covariant over their // lifetime. reborrow_generic_mut_into_mut::(my_mut, other_mut); } } }