7 releases
new 0.3.0 | Mar 25, 2025 |
---|---|
0.2.0 | Mar 17, 2025 |
0.1.0 |
|
0.0.4 | Mar 2, 2025 |
0.0.0 | Nov 24, 2024 |
#454 in Parser implementations
294 downloads per month
175KB
3.5K
SLoC
Refined
Simple refinement types for Rust.
A basic introduction to the library is available on my blog.
For detailed information, please see the documentation on docs.rs.
Features
- Serde integration
- Logical implication for most predicates
- Zero-overhead arithmetic
- Stateful refinement
- Run-time performance optimization
Example
use refined::{prelude::*, boolean::And, boundable::unsigned::{ClosedInterval, NonZero}, string::Trimmed};
use serde::{Serialize, Deserialize};
use serde_json::{json, from_value};
type MovieRating = Refinement<u8, ClosedInterval<1, 10>>;
type NonEmptyString = Refinement<String, And<Trimmed, NonZero>>;
#[derive(Debug, Serialize, Deserialize)]
struct Movie {
title: NonEmptyString,
director: NonEmptyString,
rating: MovieRating
}
fn main() {
let movie: Movie = from_value(json!({
"title": "V for Vendetta",
"director": "James McTeigue",
"rating": 10
})).unwrap();
let malformed_movie: Movie = from_value(json!({
"title": "Missing a director",
"director": "",
"rating": 1
}));
assert!(malformed_movie.is_err());
}
Quickstart
The basic usage example on docs.rs is a minimal example that should be easy to follow.
You can also use the examples to get
started. Each example is a complete cargo project of its own. They are meant to be run with
cargo run
so that you can view their output and reference it against the code.
FAQ
What is the difference between refined
and other similar libraries?
There are a number of pre-existing libraries with a similar aim to refined
. While I make no
assertion that refined
is in any way "superior" to these other libraries when it comes to the
functionality that they provide, I had three principles in mind during development that I believe
are not met by any other library:
- Simplicity: a design that anyone should be able to look at and understand. This immediately rules out any approach that relies upon proc macros
- Maintainability: it should be simple to keep the library up to date, add functionality, fix bugs, etc. Other developers should be able to contribute to the project without difficulty
- Extensbility: downstream consumers of the library should be able to easily add their own
extensions without requiring contribution to the core
refined
library - Ease of use: downstream consumers should be able to get up and running quickly and easy. For the most common use cases, functionality should be provided by the library directly
A direct comparison against some of the more popular options:
- nutype: entirely built around proc macros. I think this is
a very cool project, and the proc macro approach might be more powerful than what
refined
is able to achieve, but there is too much "magic" involved for my liking. I'd like my types to be easy to understand and modify - refined_type and
deranged: requires explicit implementations for every
rule and type combination; for me, this is a significant impediment to both maintainability and
extensibility. Macro-based "combiners" also fall outside of my goals for
refined
- prae: more magical even than nutype. Again, a cool library, but I do not want to write a macro DSL to define my types
- ranged_integers and
light_ranged_integers: some really
cool ideas around automatically choosing the most efficient storage given the user's required
range and "op modes" that allow the user to specify how refinement should behave in the event of
failure; however, as a result, these libraries support refinement only of integer types.
ranged_integers
also relies heavily onunsafe
behavior for its core functionality, which is not something that I want forrefined
(where allunsafe
behavior is opt-in behind feature flags) - refinement: a very simple library providing basic
Refinement
andPredicate
functionality; doesn't exposePredicate
implementations that downstream users can rely upon, so the goals here are not very similar torefined
, which aims to be batteries included for the most common use cases
Ultimately, it comes down to a matter of style and taste. All of these libraries function well, and the same end goal can be achieved using any of them. The real question for users is "Which style of interaction with a library do you prefer?".
There is also the pending feature for
pattern types built into the language. An
introduction of this proposal can be found in
this gist. Pattern types fulfill
many of the goals of refined
; depending on the details of the feature once it's released, it's
possible that a subset of the use cases for refined
should be deprecated in favor of pattern
types. It seems unlikely, however, that some of the more advanced features of refined
will be
implemented with pattern types; implication
and arithmetic
in particular I feel are unlikely to
be enshrined directly in std
.
That being said, it's difficult to predict exactly how and when pattern types will land, so this
will require more thought as the feature progresses. It's possible, for example, that the core of
refined
could be re-written using pattern types while still providing the more advanced
functionality that the library already supports. This would allow downstream users to rely on a
consistent API in their code regardless of the details of how pattern types evolve over time.
Dependencies
~0.7–1.8MB
~35K SLoC