diff --git a/Cargo.lock b/Cargo.lock index cfc90b9750b..691b504c569 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2533,7 +2533,7 @@ dependencies = [ [[package]] name = "yoke" -version = "0.1.0" +version = "0.2.0" dependencies = [ "bincode", "serde", diff --git a/provider/core/Cargo.toml b/provider/core/Cargo.toml index 36d4a262188..7fa7beaaca7 100644 --- a/provider/core/Cargo.toml +++ b/provider/core/Cargo.toml @@ -36,7 +36,7 @@ icu_locid = { version = "0.2", path = "../../components/locid" } tinystr = "0.4.5" writeable = { version = "0.2", path = "../../utils/writeable" } thiserror = "1.0" -yoke = { version = "0.1", path = "../../utils/yoke", features = ["serde"] } +yoke = { version = "0.2", path = "../../utils/yoke", features = ["serde"] } # For "provider_serde" feature erased-serde = { version = "0.3", optional = true } diff --git a/provider/core/src/data_provider/test.rs b/provider/core/src/data_provider/test.rs index e90ad8c2e11..747c3e0df12 100644 --- a/provider/core/src/data_provider/test.rs +++ b/provider/core/src/data_provider/test.rs @@ -37,6 +37,9 @@ unsafe impl<'a> Yokeable<'a> for HelloAlt { fn transform(&'a self) -> &'a Self::Output { self } + fn transform_owned(self) -> Self::Output { + self + } unsafe fn make(from: Self::Output) -> Self { from } diff --git a/provider/core/src/hello_world.rs b/provider/core/src/hello_world.rs index c7ef7025e3a..80a078d8fc7 100644 --- a/provider/core/src/hello_world.rs +++ b/provider/core/src/hello_world.rs @@ -45,6 +45,9 @@ unsafe impl<'a> Yokeable<'a> for HelloWorldV1<'static> { fn transform(&'a self) -> &'a Self::Output { self } + fn transform_owned(self) -> Self::Output { + self + } unsafe fn make(from: Self::Output) -> Self { std::mem::transmute(from) } diff --git a/provider/core/src/marker/macros.rs b/provider/core/src/marker/macros.rs index 3447abf0986..963c02ef3ca 100644 --- a/provider/core/src/marker/macros.rs +++ b/provider/core/src/marker/macros.rs @@ -33,6 +33,9 @@ macro_rules! unsafe_impl_data_marker_with_lifetime { fn transform(&'a self) -> &'a Self::Output { self } + fn transform_owned(self) -> Self::Output { + self + } unsafe fn make(from: Self::Output) -> Self { std::mem::transmute(from) } @@ -67,6 +70,9 @@ macro_rules! impl_data_marker_no_lifetime { fn transform(&'a self) -> &'a Self::Output { self } + fn transform_owned(self) -> Self::Output { + self + } unsafe fn make(from: Self::Output) -> Self { from } diff --git a/provider/core/src/marker/mod.rs b/provider/core/src/marker/mod.rs index c5702aa22de..c4945279a84 100644 --- a/provider/core/src/marker/mod.rs +++ b/provider/core/src/marker/mod.rs @@ -55,6 +55,9 @@ use yoke::Yokeable; /// # fn transform(&'a self) -> &'a Self::Output { /// # self /// # } +/// # fn transform_owned(self) -> Self::Output { +/// # self +/// # } /// # unsafe fn make(from: Self::Output) -> Self { /// # std::mem::transmute(from) /// # } diff --git a/provider/core/src/serde.rs b/provider/core/src/serde.rs index 2fd5446a4b2..52e64d7c93a 100644 --- a/provider/core/src/serde.rs +++ b/provider/core/src/serde.rs @@ -272,6 +272,15 @@ where unsafe impl<'a> Yokeable<'a> for SerdeSeDataStructWrap<'static, 'static> { type Output = SerdeSeDataStructWrap<'a, 'a>; fn transform(&'a self) -> &'a Self::Output { + // The compiler isn't able to guess the variance of the trait object, + // so we must transmute + // Note (Manishearth): this is technically unsound since SerdeDeDataStruct + // has no variance requirements. This will become a non-issue + // once Borrowed is removed (https://github.com/unicode-org/icu4x/issues/752) + unsafe { std::mem::transmute(self) } + } + fn transform_owned(self) -> Self::Output { + // (needs a transmute for the same reason as above) unsafe { std::mem::transmute(self) } } unsafe fn make(from: Self::Output) -> Self { diff --git a/utils/yoke/Cargo.toml b/utils/yoke/Cargo.toml index ddb7fca4b14..ac656f95f54 100644 --- a/utils/yoke/Cargo.toml +++ b/utils/yoke/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "yoke" -version = "0.1.0" +version = "0.2.0" description = "Abstraction allowing borrowed data to be carried along with the backing data it borrows from" authors = ["Manish Goregaokar "] edition = "2018" diff --git a/utils/yoke/src/lib.rs b/utils/yoke/src/lib.rs index 8c5b7df9c28..0cd4f767652 100644 --- a/utils/yoke/src/lib.rs +++ b/utils/yoke/src/lib.rs @@ -21,6 +21,6 @@ mod zero_copy_from; #[cfg(feature = "serde")] mod serde; -pub use crate::yoke::Yoke; +pub use crate::yoke::{CloneableCart, Yoke}; pub use crate::yokeable::Yokeable; pub use crate::zero_copy_from::ZeroCopyFrom; diff --git a/utils/yoke/src/trait_hack.rs b/utils/yoke/src/trait_hack.rs index 6f8da45fac8..e30cc50b8f6 100644 --- a/utils/yoke/src/trait_hack.rs +++ b/utils/yoke/src/trait_hack.rs @@ -42,6 +42,9 @@ //! # fn transform(&'a self) -> &'a Self::Output { //! # self //! # } +//! # fn transform_owned(self) -> Self::Output { +//! # self +//! # } //! # unsafe fn make(from: Self::Output) -> Self { //! # std::mem::transmute(from) //! # } @@ -92,6 +95,9 @@ //! # fn transform(&'a self) -> &'a Self::Output { //! # self //! # } +//! # fn transform_owned(self) -> Self::Output { +//! # self +//! # } //! # unsafe fn make(from: Self::Output) -> Self { //! # std::mem::transmute(from) //! # } @@ -154,6 +160,9 @@ //! # fn transform(&'a self) -> &'a Self::Output { //! # self //! # } +//! # fn transform_owned(self) -> Self::Output { +//! # self +//! # } //! # unsafe fn make(from: Self::Output) -> Self { //! # std::mem::transmute(from) //! # } diff --git a/utils/yoke/src/yoke.rs b/utils/yoke/src/yoke.rs index d5d277bece0..d424e3f89ec 100644 --- a/utils/yoke/src/yoke.rs +++ b/utils/yoke/src/yoke.rs @@ -4,6 +4,7 @@ use crate::Yokeable; use stable_deref_trait::StableDeref; +use std::marker::PhantomData; use std::ops::Deref; use std::rc::Rc; use std::sync::Arc; @@ -326,6 +327,11 @@ impl Yokeable<'a>, C> Yoke { /// # self /// # } /// # + /// # fn transform_owned(self) -> Bar<'a> { + /// # // covariant lifetime cast, can be done safely + /// # self + /// # } + /// # /// # unsafe fn make(from: Bar<'a>) -> Self { /// # let ret = mem::transmute_copy(&from); /// # mem::forget(from); @@ -442,9 +448,24 @@ impl Yokeable<'a>, C: StableDeref> Yoke> { } } +/// This trait marks cart types that do not change source on cloning +/// +/// This is conceptually similar to [`stable_deref_trait::CloneStableDeref`], +/// however [`stable_deref_trait::CloneStableDeref`] is not (and should not) be +/// implemented on [`Option`] (since it's not [`Deref`]). [`CloneableCart`] essentially is +/// "if there _is_ data to borrow from here, cloning the cart gives you an additional +/// handle to the same data". +pub unsafe trait CloneableCart: Clone {} + +unsafe impl CloneableCart for Rc {} +unsafe impl CloneableCart for Arc {} +unsafe impl CloneableCart for Option {} +unsafe impl<'a, T: ?Sized> CloneableCart for &'a T {} +unsafe impl CloneableCart for () {} + /// Clone requires that the cart derefs to the same address after it is cloned. This works for Rc, Arc, and &'a T. -/// For all other cart types, clone `.baking_cart()` and re-use `attach_to_cart()`. -impl Yokeable<'a>, T: ?Sized> Clone for Yoke> +/// For all other cart types, clone `.backing_cart()` and re-use `attach_to_cart()`. +impl Yokeable<'a>, C: CloneableCart> Clone for Yoke where for<'a> >::Output: Clone, { @@ -456,74 +477,271 @@ where } } -impl<'b, Y: for<'a> Yokeable<'a>, T: ?Sized> Clone for Yoke -where - for<'a> >::Output: Clone, -{ - fn clone(&self) -> Self { +impl Yokeable<'a>, C> Yoke { + /// Allows one to "project" a yoke to perform a transformation on the data, potentially + /// looking at a subfield, and producing a new yoke. This will move cart, and the provided + /// transformation is only allowed to use data known to be borrowed from the cart. + /// + /// This takes an additional `PhantomData<&()>` parameter as a workaround to the issue + /// described in [#86702](https://github.com/rust-lang/rust/issues/86702). This parameter + /// should just be ignored in the function. + /// + /// Furthermore, + /// [compiler bug #84937](https://github.com/rust-lang/rust/issues/84937) prevents + /// this from taking a capturing closure, however [`Yoke::project_with_capture()`] + /// can be used for the same use cases. + /// + /// + /// This can be used, for example, to transform data from one format to another: + /// + /// ```rust,ignore + /// # // This doctest is temporarily ignored because of https://github.com/rust-lang/rust/issues/86703 + /// # use std::rc::Rc; + /// # use yoke::Yoke; + /// # + /// fn slice(y: Yoke<&'static str, Rc<[u8]>>) -> Yoke<&'static [u8], Rc<[u8]>> { + /// y.project(move |yk, _| yk.as_bytes()) + /// } + /// + /// ``` + /// + /// This can also be used to create a yoke for a subfield + /// + /// ```rust,ignore + /// # // This doctest is temporarily ignored because of https://github.com/rust-lang/rust/issues/86703 + /// # use std::borrow::Cow; + /// # use yoke::{Yoke, Yokeable}; + /// # use std::mem; + /// # use std::rc::Rc; + /// # + /// // also safely implements Yokeable<'a> + /// struct Bar<'a> { + /// string_1: &'a str, + /// string_2: &'a str, + /// } + /// + /// fn project_string_1(bar: Yoke, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> { + /// bar.project(|bar, _| bar.string_1) + /// } + /// + /// # + /// # unsafe impl<'a> Yokeable<'a> for Bar<'static> { + /// # type Output = Bar<'a>; + /// # fn transform(&'a self) -> &'a Bar<'a> { + /// # self + /// # } + /// # + /// # fn transform_owned(self) -> Bar<'a> { + /// # // covariant lifetime cast, can be done safely + /// # self + /// # } + /// # + /// # unsafe fn make(from: Bar<'a>) -> Self { + /// # let ret = mem::transmute_copy(&from); + /// # mem::forget(from); + /// # ret + /// # } + /// # + /// # fn transform_mut(&'a mut self, f: F) + /// # where + /// # F: 'static + FnOnce(&'a mut Self::Output), + /// # { + /// # unsafe { f(mem::transmute(self)) } + /// # } + /// # } + /// ``` + // + // Safety docs can be found below on `__project_safety_docs()` + pub fn project

( + self, + f: for<'a> fn( + >::Output, + PhantomData<&'a ()>, + ) ->

>::Output, + ) -> Yoke + where + P: for<'a> Yokeable<'a>, + { + let p = f(self.yokeable.transform_owned(), PhantomData); Yoke { - yokeable: unsafe { Y::make(self.get().clone()) }, + yokeable: unsafe { P::make(p) }, cart: self.cart, } } -} -impl Yokeable<'a>, T: ?Sized> Clone for Yoke> -where - for<'a> >::Output: Clone, -{ - fn clone(&self) -> Self { + /// This is similar to [`Yoke::project`], however it does not move + /// [`Self`] and instead clones the cart (only if the cart is a [`CloneableCart`]) + /// + /// This is a bit more efficient than cloning the [`Yoke`] and then calling [`Yoke::project`] + /// because then it will not clone fields that are going to be discarded. + pub fn project_cloned<'this, P>( + &'this self, + f: for<'a> fn( + &'this >::Output, + PhantomData<&'a ()>, + ) ->

>::Output, + ) -> Yoke + where + P: for<'a> Yokeable<'a>, + C: CloneableCart, + { + let p = f(self.get(), PhantomData); Yoke { - yokeable: unsafe { Y::make(self.get().clone()) }, + yokeable: unsafe { P::make(p) }, cart: self.cart.clone(), } } -} -impl Yokeable<'a>, T: ?Sized> Clone for Yoke>> -where - for<'a> >::Output: Clone, -{ - fn clone(&self) -> Self { + /// This is similar to [`Yoke::project`], however it works around it not being able to + /// use `FnOnce` by using an explicit capture input, until [compiler bug #84937](https://github.com/rust-lang/rust/issues/84937) + /// is fixed. + /// + /// See the docs of [`Yoke::project`] for how this works. + pub fn project_with_capture( + self, + capture: T, + f: for<'a> fn( + >::Output, + capture: T, + PhantomData<&'a ()>, + ) ->

>::Output, + ) -> Yoke + where + P: for<'a> Yokeable<'a>, + { + let p = f(self.yokeable.transform_owned(), capture, PhantomData); Yoke { - yokeable: unsafe { Y::make(self.get().clone()) }, - cart: self.cart.clone(), + yokeable: unsafe { P::make(p) }, + cart: self.cart, } } -} -impl Yokeable<'a>, T: ?Sized> Clone for Yoke>> -where - for<'a> >::Output: Clone, -{ - fn clone(&self) -> Self { + /// This is similar to [`Yoke::project_cloned`], however it works around it not being able to + /// use `FnOnce` by using an explicit capture input, until [compiler bug #84937](https://github.com/rust-lang/rust/issues/84937) + /// is fixed. + /// + /// See the docs of [`Yoke::project_cloned`] for how this works. + pub fn project_cloned_with_capture<'this, P, T>( + &'this self, + capture: T, + f: for<'a> fn( + &'this >::Output, + capture: T, + PhantomData<&'a ()>, + ) ->

>::Output, + ) -> Yoke + where + P: for<'a> Yokeable<'a>, + C: CloneableCart, + { + let p = f(self.get(), capture, PhantomData); Yoke { - yokeable: unsafe { Y::make(self.get().clone()) }, + yokeable: unsafe { P::make(p) }, cart: self.cart.clone(), } } } -impl<'b, Y: for<'a> Yokeable<'a>, T: ?Sized> Clone for Yoke> -where - for<'a> >::Output: Clone, -{ - fn clone(&self) -> Self { - Yoke { - yokeable: unsafe { Y::make(self.get().clone()) }, - cart: self.cart, - } - } -} - -impl Yokeable<'a>> Clone for Yoke -where - for<'a> >::Output: Clone, -{ - fn clone(&self) -> Self { - Yoke { - yokeable: unsafe { Y::make(self.get().clone()) }, - cart: (), - } - } -} +/// Safety docs for project() +/// +/// (Docs are on a private const to allow the use of compile_fail doctests) +/// +/// This is safe to perform because of the choice of lifetimes on `f`, that is, +/// `for fn(>::Output, &'a ()) ->

>::Output`. +/// +/// What we want this function to do is take a Yokeable (`Y`) that is borrowing from the cart, and +/// produce another Yokeable (`P`) that also borrows from the same cart. There are a couple potential +/// hazards here: +/// +/// - `P` ends up borrowing data from `Y` (or elsewhere) that did _not_ come from the cart, +/// for example `P` could borrow owned data from a `Cow`. This would make the `Yoke

` dependent +/// on data owned only by the `Yoke`. +/// - Borrowed data from `Y` escapes with the wrong lifetime +/// +/// Let's walk through these and see how they're prevented. +/// +/// ```rust, compile_fail +/// # use std::rc::Rc; +/// # use yoke::Yoke; +/// # use std::borrow::Cow; +/// fn borrow_potentially_owned(y: &Yoke, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> { +/// y.project_cloned(|cow, _| &*cow) +/// } +/// ``` +/// +/// In this case, the lifetime of `&*cow` is `&'this str`, however the function needs to be able to return +/// `&'a str` _for all `'a`_, which isn't possible. +/// +/// +/// ```rust, compile_fail +/// # use std::rc::Rc; +/// # use yoke::Yoke; +/// # use std::borrow::Cow; +/// fn borrow_potentially_owned(y: Yoke, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> { +/// y.project(|cow, _| &*cow) +/// } +/// ``` +/// +/// This has the same issue, `&*cow` is borrowing for a local lifetime. +/// +/// Similarly, trying to project an owned field of a struct will produce similar errors: +/// +/// ```rust,compile_fail +/// # use std::borrow::Cow; +/// # use yoke::{Yoke, Yokeable}; +/// # use std::mem; +/// # use std::rc::Rc; +/// # +/// // also safely implements Yokeable<'a> +/// struct Bar<'a> { +/// owned: String, +/// string_2: &'a str, +/// } +/// +/// fn project_owned(bar: &Yoke, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> { +/// // ERROR (but works if you replace owned with string_2) +/// bar.project_cloned(|bar, _| &*bar.owned) +/// } +/// +/// # +/// # unsafe impl<'a> Yokeable<'a> for Bar<'static> { +/// # type Output = Bar<'a>; +/// # fn transform(&'a self) -> &'a Bar<'a> { +/// # self +/// # } +/// # +/// # fn transform_owned(self) -> Bar<'a> { +/// # // covariant lifetime cast, can be done safely +/// # self +/// # } +/// # +/// # unsafe fn make(from: Bar<'a>) -> Self { +/// # let ret = mem::transmute_copy(&from); +/// # mem::forget(from); +/// # ret +/// # } +/// # +/// # fn transform_mut(&'a mut self, f: F) +/// # where +/// # F: 'static + FnOnce(&'a mut Self::Output), +/// # { +/// # unsafe { f(mem::transmute(self)) } +/// # } +/// # } +/// ``` +/// +/// Borrowed data from `Y` similarly cannot escape with the wrong lifetime because of the `for<'a>`, since +/// it will never be valid for the borrowed data to escape for all lifetimes of 'a. Internally, `.project()` +/// uses `.get()`, however the signature forces the callers to be able to handle every lifetime. +/// +/// `'a` is the only lifetime that matters here; `Yokeable`s must be `'static` and since +/// `Output` is an associated type it can only have one lifetime, `'a` (there's nowhere for it to get another from). +/// `Yoke`s can get additional lifetimes via the cart, and indeed, `project()` can operate on `Yoke<_, &'b [u8]>`, +/// however this lifetime is inaccessible to the closure, and even if it were accessible the `for<'a>` would force +/// it out of the output. All external lifetimes (from other found outside the yoke/closures +/// are similarly constrained here. +/// +/// Essentially, safety is achieved by using `for<'a> fn(...)` with `'a` used in both `Yokeable`s to ensure that +/// the output yokeable can _only_ have borrowed data flow in to it from the input. All paths of unsoundness require the +/// unification of an existential and universal lifetime, which isn't possible. +const _: () = (); diff --git a/utils/yoke/src/yokeable.rs b/utils/yoke/src/yokeable.rs index 29dea21009d..67441d1de31 100644 --- a/utils/yoke/src/yokeable.rs +++ b/utils/yoke/src/yokeable.rs @@ -59,6 +59,11 @@ use std::{mem, ptr}; /// self /// } /// +/// fn transform_owned(self) -> Bar<'a> { +/// // covariant lifetime cast, can be done safely +/// self +/// } +/// /// unsafe fn make(from: Bar<'a>) -> Self { /// // We're just doing mem::transmute() here, however Rust is /// // not smart enough to realize that Bar<'a> and Bar<'static> are of @@ -93,6 +98,15 @@ pub unsafe trait Yokeable<'a>: 'static { /// if desired. fn transform(&'a self) -> &'a Self::Output; + /// This method must cast `self` between `Self<'static>` and `Self<'a>`. + /// + /// # Implementation safety + /// + /// If the invariants of [`Yokeable`] are being satisfied, the body of this method + /// should simply be `{ self }`, though it's acceptable to include additional assertions + /// if desired. + fn transform_owned(self) -> Self::Output; + /// This method can be used to cast away `Self<'a>`'s lifetime. /// /// # Safety @@ -166,6 +180,11 @@ pub unsafe trait Yokeable<'a>: 'static { /// # self /// # } /// # + /// # fn transform_owned(self) -> Bar<'a> { + /// # // covariant lifetime cast, can be done safely + /// # self + /// # } + /// # /// # unsafe fn make(from: Bar<'a>) -> Self { /// # let ret = mem::transmute_copy(&from); /// # mem::forget(from); @@ -218,6 +237,11 @@ where self } + fn transform_owned(self) -> Cow<'a, T> { + // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe + self + } + unsafe fn make(from: Cow<'a, T>) -> Self { // i hate this // unfortunately Rust doesn't think `mem::transmute` is possible since it's not sure the sizes @@ -244,6 +268,11 @@ unsafe impl<'a, T: 'static + ?Sized> Yokeable<'a> for &'static T { self } + fn transform_owned(self) -> &'a T { + // Doesn't need unsafe: `'a` is covariant so this lifetime cast is always safe + self + } + unsafe fn make(from: &'a T) -> Self { mem::transmute(from) } diff --git a/utils/yoke/src/zero_copy_from.rs b/utils/yoke/src/zero_copy_from.rs index 5ecac28ab42..bf393d6c7cc 100644 --- a/utils/yoke/src/zero_copy_from.rs +++ b/utils/yoke/src/zero_copy_from.rs @@ -37,6 +37,10 @@ use std::rc::Rc; /// # fn transform(&'a self) -> &'a Self::Output { /// # self /// # } +/// # fn transform_owned(self) -> MyStruct<'a> { +/// # // covariant lifetime cast, can be done safely +/// # self +/// # } /// # unsafe fn make(from: Self::Output) -> Self { /// # std::mem::transmute(from) /// # } diff --git a/utils/yoke/tests/bincode.rs b/utils/yoke/tests/bincode.rs index 5da1e90ea26..48de22610ae 100644 --- a/utils/yoke/tests/bincode.rs +++ b/utils/yoke/tests/bincode.rs @@ -40,6 +40,10 @@ unsafe impl<'a> Yokeable<'a> for Bar<'static> { self } + fn transform_owned(self) -> Bar<'a> { + self + } + unsafe fn make(from: Bar<'a>) -> Self { let ret = mem::transmute_copy(&from); mem::forget(from); diff --git a/utils/zerovec/Cargo.toml b/utils/zerovec/Cargo.toml index 65826ec007d..75bfd7b348d 100644 --- a/utils/zerovec/Cargo.toml +++ b/utils/zerovec/Cargo.toml @@ -25,7 +25,7 @@ all-features = true [dependencies] either = "1.6.1" serde = { version = "1.0", optional = true } -yoke = { path = "../yoke", version = "0.1.0", optional = true } +yoke = { path = "../yoke", version = "0.2.0", optional = true } [dev-dependencies] icu_benchmark_macros = { version = "0.2", path = "../../tools/benchmark/macros" } diff --git a/utils/zerovec/src/yoke_impls.rs b/utils/zerovec/src/yoke_impls.rs index 3651db66085..d24f68026ec 100644 --- a/utils/zerovec/src/yoke_impls.rs +++ b/utils/zerovec/src/yoke_impls.rs @@ -15,7 +15,9 @@ unsafe impl<'a, T: 'static + AsULE + ?Sized> Yokeable<'a> for ZeroVec<'static, T fn transform(&'a self) -> &'a ZeroVec<'a, T> { self } - + fn transform_owned(self) -> ZeroVec<'a, T> { + self + } unsafe fn make(from: ZeroVec<'a, T>) -> Self { debug_assert!(mem::size_of::>() == mem::size_of::()); let ptr: *const Self = (&from as *const Self::Output).cast(); @@ -38,7 +40,9 @@ unsafe impl<'a, T: 'static + AsVarULE> Yokeable<'a> for VarZeroVec<'static, T> { fn transform(&'a self) -> &'a VarZeroVec<'a, T> { self } - + fn transform_owned(self) -> VarZeroVec<'a, T> { + self + } unsafe fn make(from: VarZeroVec<'a, T>) -> Self { debug_assert!(mem::size_of::>() == mem::size_of::()); let ptr: *const Self = (&from as *const Self::Output).cast(); @@ -74,7 +78,16 @@ where mem::transmute::<&Self, &Self::Output>(self) } } - + fn transform_owned(self) -> ZeroMap<'a, K::Output, V::Output> { + debug_assert!(mem::size_of::() == mem::size_of::()); + unsafe { + // Similar problem as transform(), but we need to use ptr::read since + // the compiler isn't sure of the sizes + let ptr: *const Self::Output = (&self as *const Self).cast(); + mem::forget(self); + ptr::read(ptr) + } + } unsafe fn make(from: ZeroMap<'a, K::Output, V::Output>) -> Self { debug_assert!( mem::size_of::>() == mem::size_of::()