greyblake / nutype

Rust newtype with guarantees πŸ‡ΊπŸ‡¦ πŸ¦€
MIT License
1.42k stars 23 forks source link

allow implementing `core::error::Error` when available #184

Closed vic1707 closed 3 weeks ago

vic1707 commented 2 months ago

rust 1.81.0 made std::error::Error available in core. Users with that version or higher should be getting the Error impl no matter if std feature is enabled. fixes #179 without introducing a breaking change

vic1707 commented 2 months ago

I think its good

lib.rs

use nutype::nutype;

#[nutype(validate(finite))]
struct A(f32);

Cargo.toml

[dependencies]
nutype = { path = "../nutype/nutype" }

Outputs of cargo expand

1.81.0

`nutype = { path = "../nutype/nutype" }` => `impl ::core::error::Error for AError { /* ... */ }`
```rs #![feature(prelude_import)] #[prelude_import] use std::prelude::rust_2021::*; #[macro_use] extern crate std; use nutype::nutype; #[doc(hidden)] mod __nutype_A__ { use super::*; pub struct A(f32); #[allow(clippy::enum_variant_names)] pub enum AError { FiniteViolated, } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::fmt::Debug for AError { #[inline] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::write_str(f, "FiniteViolated") } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::clone::Clone for AError { #[inline] fn clone(&self) -> AError { AError::FiniteViolated } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::marker::StructuralPartialEq for AError {} #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::cmp::PartialEq for AError { #[inline] fn eq(&self, other: &AError) -> bool { true } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::cmp::Eq for AError { #[inline] #[doc(hidden)] #[coverage(off)] fn assert_receiver_is_total_eq(&self) -> () {} } impl ::core::fmt::Display for AError { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { AError::FiniteViolated => { f.write_fmt(format_args!("{0} is not finite.", "A")) } } } } impl ::core::error::Error for AError { fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> { None } } impl A { pub fn try_new(raw_value: f32) -> ::core::result::Result { let sanitized_value: f32 = Self::__sanitize__(raw_value); Self::__validate__(&sanitized_value)?; Ok(A(sanitized_value)) } fn __sanitize__(mut value: f32) -> f32 { value } fn __validate__(val: &f32) -> core::result::Result<(), AError> { let val = *val; if !val.is_finite() { return Err(AError::FiniteViolated); } Ok(()) } #[deprecated(since = "0.4.3", note = "\nUse `try_new` instead.")] pub fn new(raw_value: f32) -> ::core::result::Result { Self::try_new(raw_value) } } impl A { #[inline] pub fn into_inner(self) -> f32 { self.0 } } } use __nutype_A__::A; use __nutype_A__::AError; ```
`nutype = { path = "../nutype/nutype", default-features = false }` => `impl ::core::error::Error for AError { /* ... */ }`
```rs #![feature(prelude_import)] #[prelude_import] use std::prelude::rust_2021::*; #[macro_use] extern crate std; use nutype::nutype; #[doc(hidden)] mod __nutype_A__ { use super::*; pub struct A(f32); #[allow(clippy::enum_variant_names)] pub enum AError { FiniteViolated, } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::fmt::Debug for AError { #[inline] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::write_str(f, "FiniteViolated") } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::clone::Clone for AError { #[inline] fn clone(&self) -> AError { AError::FiniteViolated } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::marker::StructuralPartialEq for AError {} #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::cmp::PartialEq for AError { #[inline] fn eq(&self, other: &AError) -> bool { true } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::cmp::Eq for AError { #[inline] #[doc(hidden)] #[coverage(off)] fn assert_receiver_is_total_eq(&self) -> () {} } impl ::core::fmt::Display for AError { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { AError::FiniteViolated => { f.write_fmt(format_args!("{0} is not finite.", "A")) } } } } impl ::core::error::Error for AError { fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> { None } } impl A { pub fn try_new(raw_value: f32) -> ::core::result::Result { let sanitized_value: f32 = Self::__sanitize__(raw_value); Self::__validate__(&sanitized_value)?; Ok(A(sanitized_value)) } fn __sanitize__(mut value: f32) -> f32 { value } fn __validate__(val: &f32) -> core::result::Result<(), AError> { let val = *val; if !val.is_finite() { return Err(AError::FiniteViolated); } Ok(()) } #[deprecated(since = "0.4.3", note = "\nUse `try_new` instead.")] pub fn new(raw_value: f32) -> ::core::result::Result { Self::try_new(raw_value) } } impl A { #[inline] pub fn into_inner(self) -> f32 { self.0 } } } use __nutype_A__::A; use __nutype_A__::AError; ```

1.80.0

`nutype = { path = "../nutype/nutype" }` => `impl ::std::error::Error for AError { /* ... */ }`
```rs #![feature(prelude_import)] #[prelude_import] use std::prelude::rust_2021::*; #[macro_use] extern crate std; use nutype::nutype; #[doc(hidden)] mod __nutype_A__ { use super::*; pub struct A(f32); #[allow(clippy::enum_variant_names)] pub enum AError { FiniteViolated, } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::fmt::Debug for AError { #[inline] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::write_str(f, "FiniteViolated") } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::clone::Clone for AError { #[inline] fn clone(&self) -> AError { AError::FiniteViolated } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::marker::StructuralPartialEq for AError {} #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::cmp::PartialEq for AError { #[inline] fn eq(&self, other: &AError) -> bool { true } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::cmp::Eq for AError { #[inline] #[doc(hidden)] #[coverage(off)] fn assert_receiver_is_total_eq(&self) -> () {} } impl ::core::fmt::Display for AError { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { AError::FiniteViolated => { f.write_fmt(format_args!("{0} is not finite.", "A")) } } } } impl ::std::error::Error for AError { fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> { None } } impl A { pub fn try_new(raw_value: f32) -> ::core::result::Result { let sanitized_value: f32 = Self::__sanitize__(raw_value); Self::__validate__(&sanitized_value)?; Ok(A(sanitized_value)) } fn __sanitize__(mut value: f32) -> f32 { value } fn __validate__(val: &f32) -> core::result::Result<(), AError> { let val = *val; if !val.is_finite() { return Err(AError::FiniteViolated); } Ok(()) } #[deprecated(since = "0.4.3", note = "\nUse `try_new` instead.")] pub fn new(raw_value: f32) -> ::core::result::Result { Self::try_new(raw_value) } } impl A { #[inline] pub fn into_inner(self) -> f32 { self.0 } } } use __nutype_A__::A; use __nutype_A__::AError; ```
`nutype = { path = "../nutype/nutype", default-features = false }` => No implementation of `Error`
```rs #![feature(prelude_import)] #[prelude_import] use std::prelude::rust_2021::*; #[macro_use] extern crate std; use nutype::nutype; #[doc(hidden)] mod __nutype_A__ { use super::*; pub struct A(f32); #[allow(clippy::enum_variant_names)] pub enum AError { FiniteViolated, } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::fmt::Debug for AError { #[inline] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::write_str(f, "FiniteViolated") } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::clone::Clone for AError { #[inline] fn clone(&self) -> AError { AError::FiniteViolated } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::marker::StructuralPartialEq for AError {} #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::cmp::PartialEq for AError { #[inline] fn eq(&self, other: &AError) -> bool { true } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::cmp::Eq for AError { #[inline] #[doc(hidden)] #[coverage(off)] fn assert_receiver_is_total_eq(&self) -> () {} } impl ::core::fmt::Display for AError { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { AError::FiniteViolated => { f.write_fmt(format_args!("{0} is not finite.", "A")) } } } } impl A { pub fn try_new(raw_value: f32) -> ::core::result::Result { let sanitized_value: f32 = Self::__sanitize__(raw_value); Self::__validate__(&sanitized_value)?; Ok(A(sanitized_value)) } fn __sanitize__(mut value: f32) -> f32 { value } fn __validate__(val: &f32) -> core::result::Result<(), AError> { let val = *val; if !val.is_finite() { return Err(AError::FiniteViolated); } Ok(()) } #[deprecated(since = "0.4.3", note = "\nUse `try_new` instead.")] pub fn new(raw_value: f32) -> ::core::result::Result { Self::try_new(raw_value) } } impl A { #[inline] pub fn into_inner(self) -> f32 { self.0 } } } use __nutype_A__::A; use __nutype_A__::AError; ```

1.83.0-nightly

`nutype = { path = "../nutype/nutype" }` => `impl ::std::error::Error for AError { /* ... */ }`
```rs #![feature(prelude_import)] #[prelude_import] use std::prelude::rust_2021::*; #[macro_use] extern crate std; use nutype::nutype; #[doc(hidden)] mod __nutype_A__ { use super::*; pub struct A(f32); #[allow(clippy::enum_variant_names)] pub enum AError { FiniteViolated, } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::fmt::Debug for AError { #[inline] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::write_str(f, "FiniteViolated") } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::clone::Clone for AError { #[inline] fn clone(&self) -> AError { AError::FiniteViolated } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::marker::StructuralPartialEq for AError {} #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::cmp::PartialEq for AError { #[inline] fn eq(&self, other: &AError) -> bool { true } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::cmp::Eq for AError { #[inline] #[doc(hidden)] #[coverage(off)] fn assert_receiver_is_total_eq(&self) -> () {} } impl ::core::fmt::Display for AError { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { AError::FiniteViolated => { f.write_fmt(format_args!("{0} is not finite.", "A")) } } } } impl ::core::error::Error for AError { fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> { None } } impl A { pub fn try_new(raw_value: f32) -> ::core::result::Result { let sanitized_value: f32 = Self::__sanitize__(raw_value); Self::__validate__(&sanitized_value)?; Ok(A(sanitized_value)) } fn __sanitize__(mut value: f32) -> f32 { value } fn __validate__(val: &f32) -> core::result::Result<(), AError> { let val = *val; if !val.is_finite() { return Err(AError::FiniteViolated); } Ok(()) } #[deprecated(since = "0.4.3", note = "\nUse `try_new` instead.")] pub fn new(raw_value: f32) -> ::core::result::Result { Self::try_new(raw_value) } } impl A { #[inline] pub fn into_inner(self) -> f32 { self.0 } } } use __nutype_A__::A; use __nutype_A__::AError; ```
`nutype = { path = "../nutype/nutype", default-features = false }` => `impl ::core::error::Error for AError { /* ... */ }`
```rs #![feature(prelude_import)] #[prelude_import] use std::prelude::rust_2021::*; #[macro_use] extern crate std; use nutype::nutype; #[doc(hidden)] mod __nutype_A__ { use super::*; pub struct A(f32); #[allow(clippy::enum_variant_names)] pub enum AError { FiniteViolated, } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::fmt::Debug for AError { #[inline] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::write_str(f, "FiniteViolated") } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::clone::Clone for AError { #[inline] fn clone(&self) -> AError { AError::FiniteViolated } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::marker::StructuralPartialEq for AError {} #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::cmp::PartialEq for AError { #[inline] fn eq(&self, other: &AError) -> bool { true } } #[automatically_derived] #[allow(clippy::enum_variant_names)] impl ::core::cmp::Eq for AError { #[inline] #[doc(hidden)] #[coverage(off)] fn assert_receiver_is_total_eq(&self) -> () {} } impl ::core::fmt::Display for AError { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { AError::FiniteViolated => { f.write_fmt(format_args!("{0} is not finite.", "A")) } } } } impl ::core::error::Error for AError { fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> { None } } impl A { pub fn try_new(raw_value: f32) -> ::core::result::Result { let sanitized_value: f32 = Self::__sanitize__(raw_value); Self::__validate__(&sanitized_value)?; Ok(A(sanitized_value)) } fn __sanitize__(mut value: f32) -> f32 { value } fn __validate__(val: &f32) -> core::result::Result<(), AError> { let val = *val; if !val.is_finite() { return Err(AError::FiniteViolated); } Ok(()) } #[deprecated(since = "0.4.3", note = "\nUse `try_new` instead.")] pub fn new(raw_value: f32) -> ::core::result::Result { Self::try_new(raw_value) } } impl A { #[inline] pub fn into_inner(self) -> f32 { self.0 } } } use __nutype_A__::A; use __nutype_A__::AError; ```
greyblake commented 2 months ago

@vic1707 Hi thanks for your PRs. Just to keep you update: sorry for the delay. I had seen them and wanted to review over the weekend by did not manage to. I'll try to process them on the next weekend.

vic1707 commented 2 months ago

No problem take your time πŸ‘ Thanks for telling me

vic1707 commented 2 months ago

Added the cargo expand on nightly just to be sure I didn't ruin everything.

vic1707 commented 2 months ago

Hi @greyblake may I ask for an update ?

greyblake commented 2 months ago

Hi, sorry I am on vacation, will be back in 10 days.

On Sun, Sep 29, 2024, 18:21 Victor LEFEBVRE @.***> wrote:

Hi @greyblake https://github.com/greyblake may I ask for an update ?

β€” Reply to this email directly, view it on GitHub https://github.com/greyblake/nutype/pull/184#issuecomment-2381396546, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAA3W2E7SJ4AGSQFV5CHHQTZZALGPAVCNFSM6AAAAABOE6I4NSVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGOBRGM4TMNJUGY . You are receiving this because you were mentioned.Message ID: @.***>

vic1707 commented 1 month ago

Hi @greyblake may I ask for an update ?

greyblake commented 1 month ago

@vic1707 Sorry, I'll try to merge this on the weekend.

greyblake commented 3 weeks ago

@vic1707 Thank you for your contribution and again sorry for the delay. I've dropped 2 tiny comments. Let me know if you'd like to address them, if no, I guess I can do it.

vic1707 commented 3 weeks ago

@greyblake, just pushed a dryer version, what do you think ?

I thought of doing

let error = cfg_if! {
    if #[cfg(ERROR_IN_CORE)] {
        quote! { ::core::error::Error }
    } else {
        quote! { ::std::error::Error }
    }
};

instead of the proposed version but it's not yet supported by rust itself, see: https://github.com/rust-lang/rust/issues/15701.

Also, do you think you could publish a new version after this merge ?

greyblake commented 3 weeks ago

@vic1707 Sorry by bad, in my thinking I assumed that we could just use the core:: variant. With a fresh head, I see that it's obviously would be contra-productive and could not work with the older rust versions. Thank you very much for the very hard try to DRY it.

vic1707 commented 3 weeks ago

No problem, it helped me get a dryer version out, win-win πŸ‘. How about releasing 0.5.1 @greyblake ? Since 0.5.0 we got a few fixes and QOL improvements πŸ™ƒ