From c67aedaeb4b3665c47b21677479e40ac6fc229ef Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Fri, 4 Aug 2023 19:52:42 +0200 Subject: [PATCH 1/3] Replace DisplayAsDisplay and PathAsDisplay with AsDisplay trait MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than having separate traits implementing as_display method, replace DisplayAsDisplay and PathAsDisplay traits with a AsDisplay trait. The difference between those two traits is in the result returned by the as_display method. With AsDisplay trait this is captured by an associated type. The main motivation for the change is making it simpler to support no_std builds in the future. Previously, PathAsDisplay would have to be handled specially in such builds on the side of macro expansion. Now, thiserror-impl doesn’t need to be aware of any complications around AsDisplay since they are all contained in thiserror crate. --- impl/src/expand.rs | 32 +++++++++++------------- src/display.rs | 51 ++++++++++++++++++++++++++++---------- src/lib.rs | 2 +- tests/ui/no-display.stderr | 2 +- 4 files changed, 55 insertions(+), 32 deletions(-) diff --git a/impl/src/expand.rs b/impl/src/expand.rs index ef8eaf31..40dcded8 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -120,14 +120,7 @@ fn impl_struct(input: Struct) -> TokenStream { }) } else if let Some(display) = &input.attrs.display { display_implied_bounds = display.implied_bounds.clone(); - let use_as_display = if display.has_bonus_display { - Some(quote! { - #[allow(unused_imports)] - use thiserror::__private::{DisplayAsDisplay, PathAsDisplay}; - }) - } else { - None - }; + let use_as_display = impl_use_as_display(display.has_bonus_display); let pat = fields_pat(&input.fields); Some(quote! { #use_as_display @@ -351,19 +344,13 @@ fn impl_enum(input: Enum) -> TokenStream { let display_impl = if input.has_display() { let mut display_inferred_bounds = InferredBounds::new(); - let use_as_display = if input.variants.iter().any(|v| { + let use_as_display = input.variants.iter().any(|v| { v.attrs .display .as_ref() .map_or(false, |display| display.has_bonus_display) - }) { - Some(quote! { - #[allow(unused_imports)] - use thiserror::__private::{DisplayAsDisplay, PathAsDisplay}; - }) - } else { - None - }; + }); + let use_as_display = impl_use_as_display(use_as_display); let void_deref = if input.variants.is_empty() { Some(quote!(*)) } else { @@ -466,6 +453,17 @@ fn fields_pat(fields: &[Field]) -> TokenStream { } } +fn impl_use_as_display(needs_as_display: bool) -> Option { + if needs_as_display { + Some(quote! { + #[allow(unused_imports)] + use thiserror::__private::AsDisplay as _; + }) + } else { + None + } +} + fn from_initializer(from_field: &Field, backtrace_field: Option<&Field>) -> TokenStream { let from_member = &from_field.member; let some_source = if type_is_option(from_field.ty) { diff --git a/src/display.rs b/src/display.rs index 0eb0dd9e..bcb244e3 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,28 +1,53 @@ use std::fmt::Display; -use std::path::{self, Path, PathBuf}; +use std::path::{Path, PathBuf}; -pub trait DisplayAsDisplay { - fn as_display(&self) -> Self; +pub trait AsDisplay { + type Target: Display + ?Sized; + + fn as_display(&self) -> &Self::Target; } -impl DisplayAsDisplay for &T { - fn as_display(&self) -> Self { +impl AsDisplay for &T { + type Target = T; + + fn as_display(&self) -> &Self::Target { self } } -pub trait PathAsDisplay { - fn as_display(&self) -> path::Display<'_>; +impl AsDisplay for Path { + type Target = PathDisplay; + + #[inline(always)] + fn as_display(&self) -> &Self::Target { + PathDisplay::new(self) + } } -impl PathAsDisplay for Path { - fn as_display(&self) -> path::Display<'_> { - self.display() +impl AsDisplay for PathBuf { + type Target = PathDisplay; + + #[inline(always)] + fn as_display(&self) -> &Self::Target { + PathDisplay::new(self.as_path()) + } +} + +#[repr(transparent)] +pub struct PathDisplay(Path); + +impl PathDisplay { + #[inline(always)] + fn new(path: &Path) -> &Self { + // SAFETY: PathDisplay is repr(transparent) so casting pointers between + // it and its payload is safe. + unsafe { &*(path as *const Path as *const Self) } } } -impl PathAsDisplay for PathBuf { - fn as_display(&self) -> path::Display<'_> { - self.display() +impl Display for PathDisplay { + #[inline(always)] + fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result { + self.0.display().fmt(fmtr) } } diff --git a/src/lib.rs b/src/lib.rs index 13e1f911..63c7fcb8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,7 @@ pub use thiserror_impl::*; #[doc(hidden)] pub mod __private { pub use crate::aserror::AsDynError; - pub use crate::display::{DisplayAsDisplay, PathAsDisplay}; + pub use crate::display::AsDisplay; #[cfg(provide_any)] pub use crate::provide::ThiserrorProvide; } diff --git a/tests/ui/no-display.stderr b/tests/ui/no-display.stderr index 76818e1e..26dc8429 100644 --- a/tests/ui/no-display.stderr +++ b/tests/ui/no-display.stderr @@ -9,7 +9,7 @@ error[E0599]: the method `as_display` exists for reference `&NoDisplay`, but its | = note: the following trait bounds were not satisfied: `NoDisplay: std::fmt::Display` - which is required by `&NoDisplay: DisplayAsDisplay` + which is required by `&NoDisplay: AsDisplay` note: the trait `std::fmt::Display` must be implemented --> $RUST/core/src/fmt/mod.rs | From 5fc62e01a2454b1ed4225137da811bf7deebe20c Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Fri, 4 Aug 2023 20:12:52 +0200 Subject: [PATCH 2/3] Prefer core crate in macro expansions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefer core crate to std crate when expanding macros. Like before, the main motivation is easier support of no_std. Two symbols needed to be handled specially. Firstly, re-export std::error::Error from thiserror::__private so that macro expansion can use it without having to refer to std crate. The idea here is that in future commits, based on selected features, either std::error or core::error will be used. Secondly, leave std::backtrace::Backtrace as is since it doesn’t have no_std equivalent. Thankfully, the type is only used when user type refers to it so it should not be a hindrance for no_std support. While at it, use leading :: for core and std paths so they don’t conflict with modules of the same name. --- impl/src/attr.rs | 4 +-- impl/src/expand.rs | 80 +++++++++++++++++++++++----------------------- src/aserror.rs | 2 +- src/lib.rs | 1 + 4 files changed, 44 insertions(+), 43 deletions(-) diff --git a/impl/src/attr.rs b/impl/src/attr.rs index aa71665d..4beb8c96 100644 --- a/impl/src/attr.rs +++ b/impl/src/attr.rs @@ -197,7 +197,7 @@ impl ToTokens for Display<'_> { let fmt = &self.fmt; let args = &self.args; tokens.extend(quote! { - std::write!(__formatter, #fmt #args) + ::core::write!(__formatter, #fmt #args) }); } } @@ -205,6 +205,6 @@ impl ToTokens for Display<'_> { impl ToTokens for Trait { fn to_tokens(&self, tokens: &mut TokenStream) { let trait_name = format_ident!("{}", format!("{:?}", self)); - tokens.extend(quote!(std::fmt::#trait_name)); + tokens.extend(quote!(::core::fmt::#trait_name)); } } diff --git a/impl/src/expand.rs b/impl/src/expand.rs index 40dcded8..ea9feff8 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -26,17 +26,17 @@ fn impl_struct(input: Struct) -> TokenStream { let source_body = if input.attrs.transparent.is_some() { let only_field = &input.fields[0]; if only_field.contains_generic { - error_inferred_bounds.insert(only_field.ty, quote!(std::error::Error)); + error_inferred_bounds.insert(only_field.ty, quote!(thiserror::__private::Error)); } let member = &only_field.member; Some(quote! { - std::error::Error::source(self.#member.as_dyn_error()) + thiserror::__private::Error::source(self.#member.as_dyn_error()) }) } else if let Some(source_field) = input.source_field() { let source = &source_field.member; if source_field.contains_generic { let ty = unoptional_type(source_field.ty); - error_inferred_bounds.insert(ty, quote!(std::error::Error + 'static)); + error_inferred_bounds.insert(ty, quote!(thiserror::__private::Error + 'static)); } let asref = if type_is_option(source_field.ty) { Some(quote_spanned!(source.span()=> .as_ref()?)) @@ -45,14 +45,14 @@ fn impl_struct(input: Struct) -> TokenStream { }; let dyn_error = quote_spanned!(source.span()=> self.#source #asref.as_dyn_error()); Some(quote! { - std::option::Option::Some(#dyn_error) + ::core::option::Option::Some(#dyn_error) }) } else { None }; let source_method = source_body.map(|body| { quote! { - fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { + fn source(&self) -> ::core::option::Option<&(dyn thiserror::__private::Error + 'static)> { use thiserror::__private::AsDynError; #body } @@ -66,7 +66,7 @@ fn impl_struct(input: Struct) -> TokenStream { let source = &source_field.member; let source_provide = if type_is_option(source_field.ty) { quote_spanned! {source.span()=> - if let std::option::Option::Some(source) = &self.#source { + if let ::core::option::Option::Some(source) = &self.#source { source.thiserror_provide(#demand); } } @@ -79,13 +79,13 @@ fn impl_struct(input: Struct) -> TokenStream { None } else if type_is_option(backtrace_field.ty) { Some(quote! { - if let std::option::Option::Some(backtrace) = &self.#backtrace { - #demand.provide_ref::(backtrace); + if let ::core::option::Option::Some(backtrace) = &self.#backtrace { + #demand.provide_ref::<::std::backtrace::Backtrace>(backtrace); } }) } else { Some(quote! { - #demand.provide_ref::(&self.#backtrace); + #demand.provide_ref::<::std::backtrace::Backtrace>(&self.#backtrace); }) }; quote! { @@ -95,17 +95,17 @@ fn impl_struct(input: Struct) -> TokenStream { } } else if type_is_option(backtrace_field.ty) { quote! { - if let std::option::Option::Some(backtrace) = &self.#backtrace { - #demand.provide_ref::(backtrace); + if let ::core::option::Option::Some(backtrace) = &self.#backtrace { + #demand.provide_ref::<::std::backtrace::Backtrace>(backtrace); } } } else { quote! { - #demand.provide_ref::(&self.#backtrace); + #demand.provide_ref::<::std::backtrace::Backtrace>(&self.#backtrace); } }; quote! { - fn provide<'_demand>(&'_demand self, #demand: &mut std::any::Demand<'_demand>) { + fn provide<'_demand>(&'_demand self, #demand: &mut ::core::any::Demand<'_demand>) { #body } } @@ -116,7 +116,7 @@ fn impl_struct(input: Struct) -> TokenStream { let only_field = &input.fields[0].member; display_implied_bounds.insert((0, Trait::Display)); Some(quote! { - std::fmt::Display::fmt(&self.#only_field, __formatter) + ::core::fmt::Display::fmt(&self.#only_field, __formatter) }) } else if let Some(display) = &input.attrs.display { display_implied_bounds = display.implied_bounds.clone(); @@ -142,9 +142,9 @@ fn impl_struct(input: Struct) -> TokenStream { let display_where_clause = display_inferred_bounds.augment_where_clause(input.generics); quote! { #[allow(unused_qualifications)] - impl #impl_generics std::fmt::Display for #ty #ty_generics #display_where_clause { + impl #impl_generics ::core::fmt::Display for #ty #ty_generics #display_where_clause { #[allow(clippy::used_underscore_binding)] - fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { #body } } @@ -157,7 +157,7 @@ fn impl_struct(input: Struct) -> TokenStream { let body = from_initializer(from_field, backtrace_field); quote! { #[allow(unused_qualifications)] - impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause { + impl #impl_generics ::core::convert::From<#from> for #ty #ty_generics #where_clause { #[allow(deprecated)] fn from(source: #from) -> Self { #ty #body @@ -196,10 +196,10 @@ fn impl_enum(input: Enum) -> TokenStream { if variant.attrs.transparent.is_some() { let only_field = &variant.fields[0]; if only_field.contains_generic { - error_inferred_bounds.insert(only_field.ty, quote!(std::error::Error)); + error_inferred_bounds.insert(only_field.ty, quote!(thiserror::__private::Error)); } let member = &only_field.member; - let source = quote!(std::error::Error::source(transparent.as_dyn_error())); + let source = quote!(thiserror::__private::Error::source(transparent.as_dyn_error())); quote! { #ty::#ident {#member: transparent} => #source, } @@ -207,7 +207,7 @@ fn impl_enum(input: Enum) -> TokenStream { let source = &source_field.member; if source_field.contains_generic { let ty = unoptional_type(source_field.ty); - error_inferred_bounds.insert(ty, quote!(std::error::Error + 'static)); + error_inferred_bounds.insert(ty, quote!(thiserror::__private::Error + 'static)); } let asref = if type_is_option(source_field.ty) { Some(quote_spanned!(source.span()=> .as_ref()?)) @@ -217,16 +217,16 @@ fn impl_enum(input: Enum) -> TokenStream { let varsource = quote!(source); let dyn_error = quote_spanned!(source.span()=> #varsource #asref.as_dyn_error()); quote! { - #ty::#ident {#source: #varsource, ..} => std::option::Option::Some(#dyn_error), + #ty::#ident {#source: #varsource, ..} => ::core::option::Option::Some(#dyn_error), } } else { quote! { - #ty::#ident {..} => std::option::Option::None, + #ty::#ident {..} => ::core::option::Option::None, } } }); Some(quote! { - fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { + fn source(&self) -> ::core::option::Option<&(dyn thiserror::__private::Error + 'static)> { use thiserror::__private::AsDynError; #[allow(deprecated)] match self { @@ -251,7 +251,7 @@ fn impl_enum(input: Enum) -> TokenStream { let varsource = quote!(source); let source_provide = if type_is_option(source_field.ty) { quote_spanned! {source.span()=> - if let std::option::Option::Some(source) = #varsource { + if let ::core::option::Option::Some(source) = #varsource { source.thiserror_provide(#demand); } } @@ -262,13 +262,13 @@ fn impl_enum(input: Enum) -> TokenStream { }; let self_provide = if type_is_option(backtrace_field.ty) { quote! { - if let std::option::Option::Some(backtrace) = backtrace { - #demand.provide_ref::(backtrace); + if let ::core::option::Option::Some(backtrace) = backtrace { + #demand.provide_ref::<::std::backtrace::Backtrace>(backtrace); } } } else { quote! { - #demand.provide_ref::(backtrace); + #demand.provide_ref::<::std::backtrace::Backtrace>(backtrace); } }; quote! { @@ -290,7 +290,7 @@ fn impl_enum(input: Enum) -> TokenStream { let varsource = quote!(source); let source_provide = if type_is_option(source_field.ty) { quote_spanned! {backtrace.span()=> - if let std::option::Option::Some(source) = #varsource { + if let ::core::option::Option::Some(source) = #varsource { source.thiserror_provide(#demand); } } @@ -310,13 +310,13 @@ fn impl_enum(input: Enum) -> TokenStream { let backtrace = &backtrace_field.member; let body = if type_is_option(backtrace_field.ty) { quote! { - if let std::option::Option::Some(backtrace) = backtrace { - #demand.provide_ref::(backtrace); + if let ::core::option::Option::Some(backtrace) = backtrace { + #demand.provide_ref::<::std::backtrace::Backtrace>(backtrace); } } } else { quote! { - #demand.provide_ref::(backtrace); + #demand.provide_ref::<::std::backtrace::Backtrace>(backtrace); } }; quote! { @@ -331,7 +331,7 @@ fn impl_enum(input: Enum) -> TokenStream { } }); Some(quote! { - fn provide<'_demand>(&'_demand self, #demand: &mut std::any::Demand<'_demand>) { + fn provide<'_demand>(&'_demand self, #demand: &mut ::core::any::Demand<'_demand>) { #[allow(deprecated)] match self { #(#arms)* @@ -369,7 +369,7 @@ fn impl_enum(input: Enum) -> TokenStream { Member::Unnamed(index) => format_ident!("_{}", index), }; display_implied_bounds.insert((0, Trait::Display)); - quote!(std::fmt::Display::fmt(#only_field, __formatter)) + quote!(::core::fmt::Display::fmt(#only_field, __formatter)) } }; for (field, bound) in display_implied_bounds { @@ -388,8 +388,8 @@ fn impl_enum(input: Enum) -> TokenStream { let display_where_clause = display_inferred_bounds.augment_where_clause(input.generics); Some(quote! { #[allow(unused_qualifications)] - impl #impl_generics std::fmt::Display for #ty #ty_generics #display_where_clause { - fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + impl #impl_generics ::core::fmt::Display for #ty #ty_generics #display_where_clause { + fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { #use_as_display #[allow(unused_variables, deprecated, clippy::used_underscore_binding)] match #void_deref self { @@ -410,7 +410,7 @@ fn impl_enum(input: Enum) -> TokenStream { let body = from_initializer(from_field, backtrace_field); Some(quote! { #[allow(unused_qualifications)] - impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause { + impl #impl_generics ::core::convert::From<#from> for #ty #ty_generics #where_clause { #[allow(deprecated)] fn from(source: #from) -> Self { #ty::#variant #body @@ -467,7 +467,7 @@ fn impl_use_as_display(needs_as_display: bool) -> Option { fn from_initializer(from_field: &Field, backtrace_field: Option<&Field>) -> TokenStream { let from_member = &from_field.member; let some_source = if type_is_option(from_field.ty) { - quote!(std::option::Option::Some(source)) + quote!(::core::option::Option::Some(source)) } else { quote!(source) }; @@ -475,11 +475,11 @@ fn from_initializer(from_field: &Field, backtrace_field: Option<&Field>) -> Toke let backtrace_member = &backtrace_field.member; if type_is_option(backtrace_field.ty) { quote! { - #backtrace_member: std::option::Option::Some(std::backtrace::Backtrace::capture()), + #backtrace_member: ::core::option::Option::Some(::std::backtrace::Backtrace::capture()), } } else { quote! { - #backtrace_member: std::convert::From::from(std::backtrace::Backtrace::capture()), + #backtrace_member: ::core::convert::From::from(::std::backtrace::Backtrace::capture()), } } }); @@ -537,7 +537,7 @@ fn spanned_error_trait(input: &DeriveInput) -> TokenStream { }; let first_span = vis_span.unwrap_or(data_span); let last_span = input.ident.span(); - let path = quote_spanned!(first_span=> std::error::); + let path = quote_spanned!(first_span=> thiserror::__private::); let error = quote_spanned!(last_span=> Error); quote!(#path #error) } diff --git a/src/aserror.rs b/src/aserror.rs index 5fea84ef..e707af5d 100644 --- a/src/aserror.rs +++ b/src/aserror.rs @@ -1,4 +1,4 @@ -use std::error::Error; +use crate::__private::Error; use std::panic::UnwindSafe; pub trait AsDynError<'a>: Sealed { diff --git a/src/lib.rs b/src/lib.rs index 63c7fcb8..37203777 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -252,4 +252,5 @@ pub mod __private { pub use crate::display::AsDisplay; #[cfg(provide_any)] pub use crate::provide::ThiserrorProvide; + pub use std::error::Error; } From c7cf56385fa8e57d07d0c097be056ef24918298e Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Sat, 5 Aug 2023 00:57:07 +0200 Subject: [PATCH 3/3] Add no_std support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce no_std feature which, when enabled, uses core::error::Error trait rather than std::error::Error and thus makes it possible to use the crate in no_std builds. Importantly though, using core::error module requires a unstable error_in_core feature thus enabling no_std requires building with nightly compiler. Furthermore, enabling `no_std` disables handling of Backtrace fields and support for displaying paths. Since those types aren’t available in no_std environments this shouldn’t be an issue. --- Cargo.toml | 3 +++ src/aserror.rs | 2 +- src/display.rs | 58 +++++++++++++++++++++++++++----------------------- src/lib.rs | 5 +++++ src/provide.rs | 2 +- 5 files changed, 41 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6505ef14..44373326 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,6 @@ members = ["impl"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = ["--generate-link-to-definition"] + +[features] +no_std = [] diff --git a/src/aserror.rs b/src/aserror.rs index e707af5d..e12d4c3f 100644 --- a/src/aserror.rs +++ b/src/aserror.rs @@ -1,5 +1,5 @@ use crate::__private::Error; -use std::panic::UnwindSafe; +use core::panic::UnwindSafe; pub trait AsDynError<'a>: Sealed { fn as_dyn_error(&self) -> &(dyn Error + 'a); diff --git a/src/display.rs b/src/display.rs index bcb244e3..40f1b543 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,5 +1,4 @@ -use std::fmt::Display; -use std::path::{Path, PathBuf}; +use core::fmt::Display; pub trait AsDisplay { type Target: Display + ?Sized; @@ -15,39 +14,44 @@ impl AsDisplay for &T { } } -impl AsDisplay for Path { - type Target = PathDisplay; +#[cfg(not(feature = "no_std"))] +mod path { + use std::path::{Path, PathBuf}; - #[inline(always)] - fn as_display(&self) -> &Self::Target { - PathDisplay::new(self) + impl super::AsDisplay for Path { + type Target = PathDisplay; + + #[inline(always)] + fn as_display(&self) -> &Self::Target { + PathDisplay::new(self) + } } -} -impl AsDisplay for PathBuf { - type Target = PathDisplay; + impl super::AsDisplay for PathBuf { + type Target = PathDisplay; - #[inline(always)] - fn as_display(&self) -> &Self::Target { - PathDisplay::new(self.as_path()) + #[inline(always)] + fn as_display(&self) -> &Self::Target { + PathDisplay::new(self.as_path()) + } } -} -#[repr(transparent)] -pub struct PathDisplay(Path); + #[repr(transparent)] + pub struct PathDisplay(Path); -impl PathDisplay { - #[inline(always)] - fn new(path: &Path) -> &Self { - // SAFETY: PathDisplay is repr(transparent) so casting pointers between - // it and its payload is safe. - unsafe { &*(path as *const Path as *const Self) } + impl PathDisplay { + #[inline(always)] + fn new(path: &Path) -> &Self { + // SAFETY: PathDisplay is repr(transparent) so casting pointers + // between it and its payload is safe. + unsafe { &*(path as *const Path as *const Self) } + } } -} -impl Display for PathDisplay { - #[inline(always)] - fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result { - self.0.display().fmt(fmtr) + impl core::fmt::Display for PathDisplay { + #[inline(always)] + fn fmt(&self, fmtr: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.display().fmt(fmtr) + } } } diff --git a/src/lib.rs b/src/lib.rs index 37203777..4c1c9fb2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -237,6 +237,8 @@ clippy::wildcard_imports, )] #![cfg_attr(provide_any, feature(provide_any))] +#![cfg_attr(feature = "no_std", feature(error_in_core))] +#![cfg_attr(feature = "no_std", no_std)] mod aserror; mod display; @@ -252,5 +254,8 @@ pub mod __private { pub use crate::display::AsDisplay; #[cfg(provide_any)] pub use crate::provide::ThiserrorProvide; + #[cfg(feature = "no_std")] + pub use core::error::Error; + #[cfg(not(feature = "no_std"))] pub use std::error::Error; } diff --git a/src/provide.rs b/src/provide.rs index 524e7435..3f772bda 100644 --- a/src/provide.rs +++ b/src/provide.rs @@ -1,4 +1,4 @@ -use std::any::{Demand, Provider}; +use core::any::{Demand, Provider}; pub trait ThiserrorProvide: Sealed { fn thiserror_provide<'a>(&'a self, demand: &mut Demand<'a>);