greyblake / nutype

Rust newtype with guarantees 🇺🇦 🦀

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support for borrowing newtypes

Ducky2048 opened this issue · comments

First of all, thank you for this library! I like it a lot.

I have a request:

I would like to make newtypes containing borrowed values, but it appears that doesn't work.

For example, this:

#[nutype(derive(Debug))]
struct A<'a>(Cow<'a, str>);

Expands to this:

#[doc(hidden)]
mod __nutype_private_A__ {
    use super::*;

    #[derive(Debug, )]
   /// Lifetime on A is missing
    pub struct A(Cow<'a, str>);

    impl A {
        pub fn new(raw_value: Cow<'a, str>) -> Self {
            fn sanitize(mut value: Cow<'a, str>) -> Cow<'a, str> { value }
            Self(sanitize(raw_value))
        }
    }

    impl A { pub fn into_inner(self) -> Cow<'a, str> { self.0 } }
}
use __nutype_private_A__::A

The important part is that the lifetime declaration on A seems to be lost. Is it possible to make this work?

Hi, thanks for reporting the issue.
Currently generics are not yet support, but I plan to add support for this.
There is a related issue to this: #130

The basics implementation to support generics is merged in #135

There some corner cases that need to be addressed before a new version can be published, see generics label.

@Ducky2048 If would appreciate if you can try the current implementation out and let me know if there something else missing!

It works perfectly for what I initially asked for, which is great!

What I found is that automatically deriving Serialize and Deserialize doesn't seem to work:

#[nutype(
    derive(
        Debug,
        Serialize,
        Deserialize,
    ),
)]
pub struct Borrowing<'a>(Cow<'a, str>);

Expands to:

#[derive(Debug, )]
pub struct Borrowing<'a> (Cow<'a, str>);
// perfect
impl<'a> Borrowing<'a> {
    pub fn new(raw_value: Cow<'a, str>) -> Self { Self(Self::__sanitize__(raw_value)) }
    fn __sanitize__(mut value: Cow<'a, str>) -> Cow<'a, str> { value }
}

// also perfect
impl<'a> Borrowing<'a> {
    #[inline]
    pub fn into_inner(self) -> Cow<'a, str> { self.0 }
}

// missing lifetime
impl ::serde::Serialize for Borrowing { fn serialize<S>(&self, serializer: S) -> ::core::result::Result<S::Ok, S::Error> where S: ::serde::Serializer { serializer.serialize_newtype_struct("Borrowing", &self.0) } }

// missing lifetime
impl<'de> ::serde::Deserialize<'de> for Borrowing {
    fn deserialize<D: ::serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        struct __Visitor<'de> {
            marker: ::std::marker::PhantomData<Borrowing>,
            lifetime: ::std::marker::PhantomData<&'de ()>,
        }
        impl<'de> ::serde::de::Visitor<'de> for __Visitor<'de> {
            type Value = Borrowing;
            fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { formatter.write_fmt(::core::format_args!("tuple struct Borrowing")) }
            fn visit_newtype_struct<DE>(self, deserializer: DE) -> Result<Self::Value, DE::Error> where DE: ::serde::Deserializer<'de> {
                let raw_value: Cow<'a, str> = match <Cow<'a, str> as ::serde::Deserialize>::deserialize(deserializer) {
                    Ok(val) => val,
                    Err(err) => return Err(err)
                };
                Ok(Borrowing::new(raw_value))
            }
        }
        ::serde::de::Deserializer::deserialize_newtype_struct(deserializer, "Borrowing", __Visitor { marker: Default::default(), lifetime: Default::default() })
    }
}

@Ducky2048 Thanks for the feedback!
Some traits cannot be derived yet properly, here is issue for to make serde traits work with generics: #141
I hope to address rather sooner than later.

@Ducky2048 Deserialize & Serialize for Cow<'a, str> should work (merged in #144).
Note, that it's not yet the full generics support for the serde traits (e.g. It won't yet work with things like NonEmpty<T>)

@greyblake Thanks a lot! I just donated 100 € for humanitarian aid to Ukraine. It's a miniscule amount in comparison to what the country needs, but maybe it helps just a little.

@Ducky2048 That's very nice of you! Thank you very much! ❤️

@Ducky2048 I just finished the work with generics and published 0.4.3-beta.1.
I am planning to release 0.4.3 in a week.