Comments (16)
I had an idea for this one: We could accept the path to an error type as an attribute and use that in the build function return type.
#[derive(Builder)]
#[builder(build_fn(error = "crate::Error"))]
pub struct MyStruct { my_field: String }
As long as there's an Into
conversion from String
to crate::Error
, that'd work (or alternatively we could let the user specify both a type and a conversion function).
This way, derive_builder
doesn't have to export anything, but the deriving type can have its build fn can use its own crate's error type and still have an autogenerated build method.
from rust-derive-builder.
I'd go with 3. I'm not sure how much code for the error type we are talking about, but I'd be surprised if it ended up being more than 50 lines or so, with most of them trait impls.
I wouldn't worry about the "matching on errors" case, as it's not really possible right now anyway. (Aside from comparing the strings, which, given a Display impl, users can still do.)
from rust-derive-builder.
I'm working on an embedded crate at the moment (no_std and no allocator, so no String
). I may find some time to work on this enhancement. I'll post an update if I start work.
from rust-derive-builder.
Ok. Hm, if someone wants to wrap that error they would either need one variant for each builder FooBuilderError
, BarBuilderError
or box it as some trait object. But then again std::error::Error
is not in core
, for some reason not obvious to me. Hmm... I guess on core, we would just implement Display
and/or Debug
then, and people could wrap that?
Here is a supposedly core + stable-compliant draft:
#[derive(Display)]
pub enum FooBuilderError<'a> {
// pre-defined errors:
UnitializedField(&'static str),
// ...
//////////////////////////////
// user-defined errors:
CustomDefault(&'static str, &'a Display),
// ^~~ by convention the first entry could be the field ident?
// ...
}
Pre-defined errors could refer to field idents via &'static str
. I don't see that pre-defined errors need to reference dynamic content, because I'm currently not planning to have pre-defined validators.
But user-defined errors should be able to do dynamic stuff. Without collections on core + stable we are then left with a reference to some trait like &Display
with finite lifetime 'a
.
So here are two problems I see with that draft:
- Since this lifetime ends up in return position of
build)
, we need to bind it to a input reference afaik. But we can only do that in the mutable/immutable pattern. How could we tell the compiler how long'a
is supposed to life? Afaik we can't have a function that's generic over some'a
in return position only with safe Rust, right? - On
std
(orcore
with collections) it would be uncomfortable to pass this error around, because the lifetime of the error would be limited to the lifetime of the builder. Users would not be able to propagate beyond that. And all wrapping error types would inherit the lifetime.
I currently think we won't get any farther than that - at least on stable + core. Do I maybe miss something?
So maybe we could have one definition which targets core
specifically and another one which targets std
and is more ergonomic?? I believe that performance should generally only be a concern on the happy path - so I wouldn't worry about allocations on the unhappy-path, but instead focus on ergonomics.
As a side note: Speaking of core
, as a workaround we could simply replace String
with &'static str
, since we know at least all the pre-defined/generated errors at compile time. But users of derive_builder
would be restricted to only inject statically known errors through hooks like custom default (or other hooks yet to come). One compromise could be, that we could switch the error type on no_std
only - or make it a no_std
-independent switch. That would not be a structured error, but at least no_std
could be used on stable.
from rust-derive-builder.
Hm, if we are generating builder-specific error definitions anyway, we could allow users some flexibility - at least in theory. I'm quite skeptical that this would blow up the public API and learning curve of derive_builder significantly.
Anyway some annotation could tell derive_builder that the custom default error will always be MyCustomDefaultError
or MyCustomDefaultErrorRef<'a>
, which you could then implement yourself. We could then collect the lifetimes of the custom error types and add it to the definition of FooBuilderError<'a, 'b, ...>
.
Currently I don't believe that this would lead to a nice API, but I may be wrong.
PS: These builder-specific errors would also be harder to migrate to a more centralized definition in the future - in case proc_macros would be allowed to export items at some point.
from rust-derive-builder.
I was thinking of a pretty abstract struct FooBuilderError(&'static str)
(not even an enum) that just contains a static error messages we can generate (e.g., "Field lorem
missing") or a hard-coded generic message (e.g., "Shit's on fire, yo").
I would not make this a complicated API at all โ the struct should probably only have one impl: AsRef<str>
to get the error message (the inner field is private). Custom errors sound way to complex for now.
from rust-derive-builder.
Ok, so basically just a newtype then. That would be more forward-compatible, if we keep adding more failure modes. With the enum based approach, each new variant would be a breaking change. So this is a strong argument for a plain newtype (or a Other
variant for all future failure modes). Ok, let's assume we do the plain newtype.
I still think the newtype could just wrap String
on std
then, no? As mentioned before, I don't consider performance the primary concern on the Err
-path if Result is used idiomatically.
I do plan to add user defined filters for fields which could then emit custom errors with dynamic information. `1277.0.0.1` is not a valid IP address, the first byte `1277` is not within the range `0...255`
instead of not a valid IP address, the first byte is not within the range `0...255`
.
We could support this with
#[cfg(no_std)]
struct FooBuilderError(String)
#[cfg(not(no_std))]
struct FooBuilderError(&'static str)
Going all &'static str
is less flexible (and a breaking change), but also less complex to document/learn/implement. :-)
I haven't made up my mind and would like to collect some possible use cases for custom error messages and see how important it is to have this flexibility. Of course the longer we wait the more painful the breaking change might be for other people..
I feel like the current custom defaults are unlikely to error. We should revisit this question at latest when we come to those user defined filters or something similar.
from rust-derive-builder.
from rust-derive-builder.
Ok, cool. :-)
We should also think about which traits we want to implement. Since we do code generation, each trait we add later would be a breaking change - the user could have already just impl'd this trait.
Error
onstd
andDisplay
/Debug
oncore
would be obvious.- I guess
Clone
wouldn't harm. - I don't know, if we want to have
AsRef<str>
and/orPartialEq
/Eq
? - I haven't done any concurrent programming with Rust, so I'm unsure about
Sync
/Send
?
from rust-derive-builder.
Hm, that question applies to the builder, too. I already had the idea to introduce a #[builder(derive(Bar))]
annotation, which would add #[derive(Bar)]
to FooBuilder
- since the ability to implement one of these traits might very well depend on the fields of Foo
. However with the errors I would like to have a fixed set, to keep it simple. :-)
from rust-derive-builder.
Hm, there may be a trick to get around the limitation of proc_macro crates not being able to export items. It would involve another crate to re-export the custom derive (+additional items). I haven't tried it yet.
from rust-derive-builder.
The error-chain
crate has looked at a lot of these issues before; it might be worth considering taking a page from that work.
The UninitializedField
variant feels weak to me, as there might be more than one field missing, and the build
method consumes the builder, which prevents the caller from inspecting it to find additional errors. Maybe the error should contain the actual builder object so that the caller can inspect it for errors?
from rust-derive-builder.
Hm, returning the builder for inspection is an interesting idea!
However, I don't think we can easily report multiple errors at once. We would need to return a vector of errors, because it could be 1...n. I believe it's better to pick and report only one error.
The error_chain
crate defines a macro by example. It is therefore able to export other items too (in contrast to proc-macro crates like derive_builder
). I haven't looked at error_chain
in detail, but at least I can say that less restrictions apply, so we may not be able to copy everything. But I'll take a closer look, thx. :-)
from rust-derive-builder.
@TedDriggs points out in #67 that there is a need to
... surface structured errors in a manner that fits with "my" crate's error-handling strategy.
I think that's an important observation.
If we introduce structured errors, what will be our story for custom structured errors? Do we require users to wrap a (private) build function or implement their own? Or is there some clever way to let users manipulate the errors we emit, including their type?
from rust-derive-builder.
FYI, I think I now know how to make a proc-macro crate that exports things: Check out from_variants for an example of it in action, courtesy of the serde team.
from rust-derive-builder.
Ok, cool. I'd be interested in your experience, because my current attempt #94 got stuck due to some no_std
trouble. I'll give you write permissions to this branch in case you want to contribute to this refactoring. :-)
from rust-derive-builder.
Related Issues (20)
- Using both `try_setter` and `setter(strip_option)` does not compile HOT 6
- Can not compile in a `#[no_std]` environment HOT 3
- Using a collection setter with mismatched collections of Vec -> HashMap causes incorrect call to be made HOT 1
- Document `each` for extending collections. HOT 1
- A shortcut for builder HOT 3
- initialize builder from existing struct instance HOT 1
- Option for generating both `strip_option` + non-`strip_option` versions of a setter? HOT 2
- Update syn dependency? HOT 9
- Feature request: infallible build() HOT 3
- Provides builder functions that consume ownership HOT 5
- Owned pattern does not compile with custom default function. HOT 5
- Question: shorthand fields HOT 3
- Potential to allow for arguments in custom validator function HOT 4
- Allow way to prevent dependency on crate `alloc`. HOT 4
- Document `custom_constructor` and `create_empty` flags
- Non-chaining `&mut` pattern HOT 3
- This library is brilliant!
- Please advise: toBuilder functionality HOT 4
- Builder + Figment HOT 5
- Feature request: `impl fn builder() -> Builder` for the struct that is derived from HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ๐๐๐
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google โค๏ธ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from rust-derive-builder.