5 releases
Uses old Rust 2015
0.0.5 | Mar 31, 2019 |
---|---|
0.0.4 | May 30, 2018 |
0.0.3 | Apr 28, 2018 |
0.0.2 | Apr 10, 2018 |
0.0.1 | Dec 5, 2017 |
#660 in Rust patterns
5,046 downloads per month
23KB
opaque_typedef
opaque_typedef:
opaque_typedef_macros:
This is a proc-macro crate for the Rust programming language.
This crate helps developers to define opaque typedef (strong typedef) types easily with less boilerplates.
NOTE: This library is under development and unstable.
Opaque typedef
You may want to define a new type, with the same internal representation as other types but without implicit type conversion. Real world example:
UncasedStr
in rocket crate (whose internal representation isstr
)NotNaN
in ordered_float crate (whose internal representation is floating point number types (usuallyf32
andf64
))
These types usually have additional restriction to internal type (UncasedStr
has looser comparison function and NotNan
cannot have NaN)
and you may want to implement some traits and don't want to implement some other traits
(for example, you may want From<&str> for &UncasedStr
but don't want Deref<Target=str> for UncasedStr
).
opaque_typedef crate helps you "derive" specific traits (i.e. reuse the traits implemented for internal types) for your type.
To see example, see files under the opaque_typedef_tests/src/
directory.
Terms
Think struct Outer(Inner);
:
- Inner type means
Inner
type. - Outer type means
Outer
type. Outer
is opaque typedef (strong typedef) ofInner
.- Unsized type means
str
,[i32]
, or something (usually slice types). - Sized type means
String
,i32
,&u8
, or something.
How to use
Examples are in opaque_typedef_tests/src/
.
1. Specify "extern crate"
Cargo.toml
:
[dependencies]
opaque_typedef = "^0.0.4"
opaque_typedef_macros = "^0.0.4"
lib.rs
or main.rs
:
extern crate opaque_typedef;
#[macro_use]
extern crate opaque_typedef_macros;
2. Derive OpaqueTypedef
for sized types, OpaqueTypedefUnsized
for unsized types
Sized type:
/// My owned string.
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, OpaqueTypedef)]
pub struct MyString(String);
Unsized type:
/// My string slice.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, OpaqueTypedefUnsized)]
#[repr(C)]
pub struct MyStr(str);
Note that #[repr(C)]
(or #[repr(transparent)]
) is necessary for unsized types.
Then you can use OpaqueTypedef
trait or OpaqueTypedefUnsized
trait.
It will be useful to implement methods for your types!
About the necessity of #[repr(*)]
, see https://github.com/lo48576/opaque_typedef/issues/1.
3. Specify if the mutable reference can be used for deriving traits (optional)
If you want opaque_typedef to derive traits who might return mutable reference to inner value (such as DerefMut
, AsMut
)
or traits who might mutate inner value (such as AddAssign
), you should specify #[opaque_typedef(allow_mut_ref)]
.
/// My string slice.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, OpaqueTypedefUnsized)]
#[repr(C)]
#[opaque_typedef(allow_mut_ref)]
pub struct MyStr(str);
If you don't specify it, opaque_typedef refuses "derive"s such as #[opaque_typedef(derive(DerefMut))]
4. "Derive" more traits
You can specify traits with #[opaque_typedef(derive(Trait1, Trait2, ...))]
.
For example:
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, OpaqueTypedefUnsized)]
#[repr(C)]
#[opaque_typedef(derive(AsciiExt, AsMut(Deref, Self), AsRef(Deref, Self), DefaultRef, Deref,
DerefMut, Display, FromInner, Into(Arc, Box, Rc, Inner),
PartialEq(Inner, InnerRev, InnerCow, InnerCowRev, SelfCow, SelfCowRev),
PartialOrd(Inner, InnerRev, InnerCow, InnerCowRev, SelfCow, SelfCowRev)))]
#[opaque_typedef(allow_mut_ref)]
pub struct MyStr(str);
Note that some traits can be shortened. Examples:
AsMutDeref
can be written asAsMut(Deref)
AsRefSelf
can be written asAsRef(Self)
IntoRc
can be written asInto(Rc)
PartialEqInner
can be written asPartialEq(Inner)
PartialOrdInner, PartialOrdSelfCow
can be written asPartialOrd(Inner, SelfCow)
To see lists of "derive"-able items, read the rest of the document or see
the source (Derive
enum in opaque_typedef_macros/src/derives/mod.rs
).
To see full list of shortened notations for "derive"-able items, see
Derive::append_from_nested_names
method at opaque_typedef_macros/src/derives/mod.rs
).
4.1. Specify deref target (optional)
If you specify Deref
, DerefMut
, AsRefDeref
or something related to Deref
, you can also specify "deref target" by #[opaque_typedef(deref(...))]
.
/// My owned string.
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, OpaqueTypedef)]
#[opaque_typedef(derive(AsMut(Deref, Inner), AsRef(Deref, Inner), Deref, DerefMut, Display,
FromInner, IntoInner, PartialEq(Inner, InnerRev),
PartialOrd(Inner, InnerRev)))]
#[opaque_typedef(deref(target = "str", deref = "String::as_str",
deref_mut = "String::as_mut_str"))]
#[opaque_typedef(allow_mut_ref)]
pub struct MyString {
inner: String,
}
Opaque_typedef uses the inner type as the default deref target type, but you can use a different type as the example above.
target
:- Deref target type.
deref
:- Conversion function from a reference to the inner type into a reference to the outer type.
- The function should implement
Fn(&Inner) -> &DerefTarget
.
deref_mut
:- Conversion function from a mutable reference to the inner type into a mutable reference to the outer type.
- The function should implement
Fn(&mut Inner) -> &mut DerefTarget
.
In the example, AsMutInner
implements AsMut<String> for MyString
, and AsMutDeref
implements AsMut<str> for MyString
.
If you don't specify #[opaque_typedef(allow_mut_ref)]
, deref_mut
would not be used and you can omit it.
5. Specify custom validator (optional)
You can specify custom validator. The value of inner type is validated on conversion into outer type. By custom validator, you can restrict the inner value.
To use custom validator, specify these attributes:
validator
- Validator function.
This should have types such as
Inner -> Result<Inner, Error>
.- For sized types,
Inner -> Result<Inner, Error>
. Validator can modify the given value and return the modified value. - For unsized types,
&Inner -> Result<&Inner, Error>
.
- For sized types,
- Validator function.
This should have types such as
error_type
- Validation error type.
Validator specified by
validator
should use this type as error. - This cannot be generic, and cannot use any type parameters of the outer types.
- Validation error type.
Validator specified by
error_msg
(optional)- Error message on panic when validation failed.
This value is used when panickable conversion failed, for example,
when invalid value is passed to
Outer::from_inner
(notOuter::try_from_inner
). - Internally,
unwrap()
will be used to panic whenerror_msg
is absent, andexpect(error_msg)
will be used whenerror_msg
is specified.
- Error message on panic when validation failed.
This value is used when panickable conversion failed, for example,
when invalid value is passed to
The example below is taken from opaque_typedef_tests/src/even32.rs
and opaque_typedef_tests/tests/even32.rs
.
/// Even `i32`.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, OpaqueTypedef)]
#[opaque_typedef(derive(Binary, Deref, Display, FromInner, PartialEq(Inner, InnerRev),
PartialOrd(Inner, InnerRev), LowerHex, Octal, UpperHex))]
#[opaque_typedef(validation(validator = "validate_even32", error_type = "OddError",
error_msg = "Failed to create `Even32`"))]
pub struct Even32(i32);
impl Even32 {
/// Returns the inner `i32` even value.
pub fn to_i32(&self) -> i32 {
self.0
}
}
/// A type of an error indicating the integer is an odd number, not even.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OddError;
fn validate_even32(v: i32) -> Result<i32, OddError> {
if v % 2 == 0 {
Ok(v)
} else {
Err(OddError)
}
}
#[cfg(test)]
mod tests {
#[test]
fn ok() {
let v = Even32::from(42);
assert_eq!(v.to_i32(), 42);
}
#[test]
#[should_panic]
fn from_odd() {
// Panics with message "Failed to create `Even32`: OddError".
let _ = Even32::from(3);
}
}
6. Specify custom comparator (optional)
You can use custom implementations for PartialEq
and PartialOrd
.
To use custom comparator, specify these attributes:
partial_eq
- Partial equality function.
This should have types such as
&Inner -> &Inner -> bool
.
- Partial equality function.
This should have types such as
partial_ord
- Partial order function.
This should have types such as
&Inner -> &Inner -> Option<::std::cmp::Ordering>
.
- Partial order function.
This should have types such as
ord
- Total order function.
This should have types such as
&Inner -> &Inner -> ::std::cmp::Ordering
. #[derive(Ord)]
doesn't usePartialOrd::partial_cmp
impl, so they can be inconsistent by mistake. Remember to keep them (includingPartialEq::eq
) consistent.
- Total order function.
This should have types such as
The example below is taken from
opaque_typedef_tests/src/reverse_order.rs
and opaque_typedef_tests/tests/reverse_order.rs
.
/// A wrapper type with reverse order.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Ord, Hash, OpaqueTypedef)]
#[opaque_typedef(derive(AsciiExt, AsMut(Deref), AsRef(Deref), Binary, Deref, DerefMut, Display,
FromInner, LowerHex, Octal, PartialOrdSelf, UpperHex))]
#[opaque_typedef(cmp(partial_ord = "(|a, b| PartialOrd::partial_cmp(a, b).map(|o| o.reverse()))",
ord = "(|a, b| Ord::cmp(a, b).reverse())"))]
#[opaque_typedef(allow_mut_ref)]
pub struct ReverseOrderSized<T>(pub T);
#[test]
fn reverse_i32() {
use std::cmp::Ordering;
assert_eq!(ReverseOrderSized(3i32).partial_cmp(&ReverseOrderSized(2i32)), Some(Ordering::Less));
assert!(ReverseOrderSized(3i32) < ReverseOrderSized(2i32));
assert_eq!(ReverseOrderSized(3i32).cmp(&ReverseOrderSized(2i32)), Ordering::Less);
assert_eq!(ReverseOrderSized(3i32).cmp(&ReverseOrderSized(3i32)), Ordering::Equal);
assert_eq!(ReverseOrderSized(3i32).cmp(&ReverseOrderSized(4i32)), Ordering::Greater);
}
Features
Defining basic constructions and casts
For sized type
#[derive(OpaqueTypedef)]
implements opaque_typedef::OpaqueTypedef
trait, and it has some basic and useful methods.
See https://docs.rs/opaque_typedef/*/opaque_typedef/trait.OpaqueTypedef.html for detail.
For unsized type
#[derive(OpaqueTypedefUnsized)]
implements opaque_typedef::OpaqueTypedefUnsized
trait, and it has some basic and useful methods.
Especially OpaqueTypedefUnsized::from_inner()
would be very useful.
See https://docs.rs/opaque_typedef/*/opaque_typedef/trait.OpaqueTypedefUnsized.html for detail.
Automatic derive for many std traits
The traits below are supported.
Note that some (such as DefaultRef
) are available only for sized types.
std::convert
, type conversion
As{Mut,Ref}{Deref,Inner,Self}
AsMutDeref
implementsAsMut<DerefTarget> for Outer
.AsMutInner
implementsAsMut<Inner> for Outer
.AsMutSelf
implementsAsMut<Outer> for Outer
.AsRefDeref
implementsAsRef<DerefTarget> for Outer
.AsRefInner
implementsAsRef<Inner> for Outer
.AsRefSelf
implementsAsRef<Self> for Outer
.
Deref
,DerefMut
Deref
implementsstd::ops::Deref for Outer
.DerefMut
implementsstd::ops::DerefMut for Outer
.
Into{Arc,Box,Inner,Rc}
,FromInner
IntoArc
implementsFrom<Outer> for Arc<Outer>
(if possible) orInto<Arc<Outer>> for Outer
.IntoBox
implementsFrom<Outer> for Box<Outer>
(if possible) orInto<Box<Outer>> for Outer
.IntoInner
implementsFrom<Outer> for Inner
.IntoRc
implementsFrom<Outer> for Rc<Outer>
(if possible) orInto<Rc<Outer>> for Outer
.FromInner
implementsFrom<Inner> for Outer
.
std::fmt
std::fmt::*
Binary
implementsstd::fmt::Binary for Outer
.Display
implementsstd::fmt::Display for Outer
.LowerExp
implementsstd::fmt::LowerExp for Outer
.LowerHex
implementsstd::fmt::LowerHex for Outer
.Octal
implementsstd::fmt::Octal for Outer
.Pointer
implementsstd::fmt::Pointer for Outer
.UpperExp
implementsstd::fmt::UpperExp for Outer
.UpperHex
implementsstd::fmt::UpperHex for Outer
.
std::cmp
Partial{Eq,Ord}{Inner,InnerCow,SelfCow}{,Rev}
PartialEqInner
implementsPartialEq<Inner> for Outer
and similar ones.PartialEqInnerRev
implementsPartialEq<Outer> for Inner
and similar ones.- This is reverse (operands order swapped) version of
PartialEqInner
.
- This is reverse (operands order swapped) version of
PartialEqInnerCow
implementsPartialEq<Cow<Inner>> for Outer
and similar ones.PartialEqInnerCowRev
implementsPartialEq<Outer> for Cow<Inner>
and similar ones.- This is reverse (operands order swapped) version of
PartialEqInnerCow
.
- This is reverse (operands order swapped) version of
PartialEqSelf
implementsPartialEq<Outer> for Outer
and similar ones.- This is very similar to
#[derive(PartialEq)]
, but it will be useful with custom comparison.
- This is very similar to
PartialEqSelfCow
implementsPartialEq<Cow<Outer>> for Outer
and similar ones.PartialEqSelfCowRev
implementsPartialEq<Outer> for Cow<Outer>
and similar ones.- This is reverse (operands order swapped) version of
PartialEqSelfCow
.
- This is reverse (operands order swapped) version of
PartialEqSelfCowAndInner
implementsPartialEq<Cow<Outer>> for Inner
and similar ones.PartialEqSelfCowAndInnerCow
implementsPartialEq<Inner> for Cow<Outer>
and similar ones.- This is reverse (operands order swapped) version of
PartialEqSelfCowAndInner
.
- This is reverse (operands order swapped) version of
PartialOrdInner
implementsPartialOrd<Inner> for Outer
and similar ones.PartialOrdInnerRev
implementsPartialOrd<Outer> for Inner
and similar ones.- This is reverse (operands order swapped) version of
PartialOrdInner
.
- This is reverse (operands order swapped) version of
PartialOrdInnerCow
implementsPartialOrd<Cow<Inner>> for Outer
and similar ones.PartialOrdInnerCowRev
implementsPartialOrd<Outer> for Cow<Inner>
and similar ones.- This is reverse (operands order swapped) version of
PartialOrdInnerCow
.
- This is reverse (operands order swapped) version of
PartialOrdSelf
implementsPartialOrd<Outer> for Outer
and similar ones.- This is very similar to
#[derive(PartialOrd)]
, but it will be useful with custom comparison.
- This is very similar to
PartialOrdSelfCow
implementsPartialOrd<Cow<Outer>> for Outer
and similar ones.PartialOrdSelfCowRev
implementsPartialOrd<Outer> for Cow<Outer>
and similar ones.- This is reverse (operands order swapped) version of
PartialOrdSelfCow
.
- This is reverse (operands order swapped) version of
PartialOrdSelfCowAndInner
implementsPartialOrd<Cow<Outer>> for Inner
and similar ones.PartialOrdSelfCowAndInnerCow
implementsPartialOrd<Inner> for Cow<Outer>
and similar ones.- This is reverse (operands order swapped) version of
PartialOrdSelfCowAndInner
.
- This is reverse (operands order swapped) version of
Ord
Ord
implementsstd::cmp::Ord for Outer
.- This is very similar to
#[derive(Ord)]
, but it will be useful with custom comparison.
- This is very similar to
std::ops
- Unary ops
Neg{,Ref}
Not{,Ref}
- Binary ops
{Add,BitAnd,BitOr,BitXor,Div,Mul,Rem,Shl,Shr,Sub}{,Assign}{,Ref}{Self,Inner,InnerRev}
Others
AsciiExt
implementsstd::ascii::AsciiExt for Outer
.DefaultRef
implementsDefault for &Outer
.
TODO
- More traits
- Nightly-only traits (
TryFrom
,TryInto
, ...) (#6)
- Nightly-only traits (
- Support types with multiple fields (#9)
License
Licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE.txt or https://www.apache.org/licenses/LICENSE-2.0 )
- MIT license (LICENSE-MIT.txt or https://opensource.org/licenses/MIT )
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.