From 414bd8034a422815c1994e92eaa699e697bfc01b Mon Sep 17 00:00:00 2001 From: Sam Rijs Date: Mon, 10 Dec 2018 21:40:01 +1100 Subject: [PATCH] introduce Safe marker trait fixes #9 --- src/lib.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d0e8b27..783a893 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ //! ``` use std::{fmt, ops, str}; +use std::default::Default; /// A UTF-8 encoded string with configurable byte storage. /// @@ -25,8 +26,13 @@ use std::{fmt, ops, str}; /// underlying byte storage, enabling it to use `Vec<[u8]>`, `&[u8]`, or third /// party types, such as [`Bytes`]. /// +/// In order to construct `String` via any of the non-unsafe constructors, +/// the backing storage needs to implement the `Safe` marker trait. +/// If you wish to construct `String` with a type that does not implement `Safe`, +/// you can use the `from_utf8_unchecked` constructor. +/// /// [`Bytes`]: https://docs.rs/bytes/0.4.8/bytes/struct.Bytes.html -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct String> { value: T, } @@ -86,7 +92,7 @@ impl String { /// let _: String> = String::from_str("nice str"); /// ``` pub fn from_str<'a>(src: &'a str) -> String - where T: From<&'a [u8]>, + where T: From<&'a [u8]> + Safe, { let value: T = src.as_bytes().into(); Self { value } @@ -118,6 +124,15 @@ impl String /// given value is valid UTF-8. /// /// Use `TryFrom` for a safe conversion. + /// + /// # Safety + /// + /// You must ensure that: + /// + /// 1. The backing storage type `T` adheres to the contract as documented on the `Safe` + /// marker trait. + /// 2. If `T` implements `AsRef<[u8]>` and/or `AsMut<[u8]>`, the byte slice returned + /// by calling `as_ref` and/or `as_mut` on the provided value represents valid utf-8. pub unsafe fn from_utf8_unchecked(value: T) -> String { String { value } } @@ -131,6 +146,8 @@ impl ops::Deref for String #[inline] fn deref(&self) -> &str { let b = self.value.as_ref(); + // SAFETY: The `Safe` marker trait ensures that + // the impl of `AsRef<[u8]>` for `T` behaves sanely. unsafe { str::from_utf8_unchecked(b) } } } @@ -141,10 +158,20 @@ impl ops::DerefMut for String #[inline] fn deref_mut(&mut self) -> &mut str { let b = self.value.as_mut(); + // SAFETY: The `Safe` marker trait ensures that + // the impl of `AsMut<[u8]>` for `T` behaves sanely. unsafe { str::from_utf8_unchecked_mut(b) } } } +impl Default for String + where T: Default + Safe +{ + fn default() -> Self { + String { value: T::default() } + } +} + impl From<::std::string::String> for String<::std::string::String> { fn from(value: ::std::string::String) -> Self { String { value } @@ -158,7 +185,7 @@ impl<'a> From<&'a str> for String<&'a str> { } impl TryFrom for String - where T: AsRef<[u8]> + where T: AsRef<[u8]> + Safe { type Error = str::Utf8Error; @@ -200,6 +227,50 @@ mod sealed { pub trait Sealed {} } +/// Marker trait that indicates that a type is guaranteed safe to use as backing storage +/// for `String`. +/// +/// In order to be safe, a storage type `T` needs to guarantee the following: +/// +/// - If `T` implements `AsRef<[u8]>` and/or `AsMut<[u8]>`, the contents of `T` as visible +/// the byte slice returned by `as_ref` and `as_mut` may only be mutated through mutable +/// references or owned access. In other words, no use of interior mutability. +/// +/// - If `T` implements `AsRef<[u8]>`, the `as_ref` method must always return the same +/// slice of bytes (unless the storage is mutated). +/// +/// - If `T` implements `AsRef<[u8]>` and `AsMut<[u8]>`, the `as_mut` method must return +/// a mutable reference to the same slice of bytes as the `as_ref` method returns. +/// +/// - If `T` implements `AsRef<[u8]>` and `Default`, the default value must represent the +/// empty byte sequence. In other words, `T::default().as_ref().len() == 0`. +/// +/// - If `T` implements `AsRef<[u8]>` and `From<&[u8]>`, it must do so in such a way that +/// the byte slice returned by `as_ref` is equal to the byte slice provided to the `from` +/// method. +pub unsafe trait Safe {} + +unsafe impl<'a, T> Safe for &'a T where T: Safe {} +unsafe impl<'a, T> Safe for &'a mut T where T: Safe {} +unsafe impl Safe for Box where T: Safe {} +unsafe impl Safe for std::rc::Rc where T: Safe {} +unsafe impl Safe for std::sync::Arc where T: Safe {} + +unsafe impl Safe for std::string::String {} +unsafe impl Safe for str {} +unsafe impl Safe for Vec {} +unsafe impl Safe for [u8] {} + +macro_rules! array_impls { + ($($len:expr)+) => { + $( + unsafe impl Safe for [u8; $len] {} + )+ + } +} + +array_impls!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16); + #[cfg(test)] mod test { use super::*;