Crates.io | impl-tools |
lib.rs | impl-tools |
version | |
source | src |
created_at | 2022-03-21 16:33:14.559507 |
updated_at | 2024-10-21 19:21:26.664231 |
description | Helper macros: autoimpl |
homepage | |
repository | https://github.com/kas-gui/impl-tools |
max_upload_size | |
id | 554187 |
Cargo.toml error: | TOML parse error at line 19, column 1 | 19 | autolib = false | ^^^^^^^ unknown field `autolib`, expected one of `name`, `version`, `edition`, `authors`, `description`, `readme`, `license`, `repository`, `homepage`, `documentation`, `build`, `resolver`, `links`, `default-run`, `default_dash_run`, `rust-version`, `rust_dash_version`, `rust_version`, `license-file`, `license_dash_file`, `license_file`, `licenseFile`, `license_capital_file`, `forced-target`, `forced_dash_target`, `autobins`, `autotests`, `autoexamples`, `autobenches`, `publish`, `metadata`, `keywords`, `categories`, `exclude`, `include` |
size | 0 |
A set of helper macros
#[autoimpl]
is a partial replacement for #[derive]
, supporting:
where
clause on generic parametersDeref
by using
a named fieldDebug
may ignore
named fields#[autoimpl]
may also be used on trait definitions to impl for specified types
supporting Deref
.
Unlike alternatives, #[autoimpl]
has minimal and intuitive syntax.
use impl_tools::autoimpl;
use std::fmt::Debug;
// Impl Animal for Box<T> where T: Animal + ?Sized
#[autoimpl(for<T: trait + ?Sized> Box<T>)]
trait Animal {
fn number_of_legs(&self) -> u32;
}
// Impl Debug for Named<T, A: Animal> omitting field animal from output
#[autoimpl(Debug ignore self.animal where T: Debug)]
// Impl Deref and DerefMut to field animal for Named<T, A: Animal>
#[autoimpl(Deref, DerefMut using self.animal)]
struct Named<T, A: Animal> {
name: T,
animal: A,
}
fn main() {
struct Fish;
impl Animal for Fish {
fn number_of_legs(&self) -> u32 {
0
}
}
let my_fish = Named {
name: "Nemo",
animal: Box::new(Fish),
};
assert_eq!(
format!("{my_fish:?} has {} legs!", my_fish.number_of_legs()),
r#"Named { name: "Nemo", .. } has 0 legs!"#
);
}
A combination of Deref
on the new-type and trait-reimplementation on the
trait allows succinct new-type patterns:
use impl_tools::autoimpl;
use std::sync::Arc;
// Impl Foo for &T, &mut T and Arc<T>
#[autoimpl(for<T: trait + ?Sized> &T, &mut T, Arc<T>)]
// Optional: impl Foo for NewFoo (requires NewFoo: Deref<Target = T>)
#[autoimpl(for<T: trait> NewFoo<T>)]
pub trait Foo {
fn success(&self) -> bool;
}
// Impl Deref and DerefMut to a Target which itself supports Foo
#[autoimpl(Deref<Target = T>, DerefMut using self.0)]
pub struct NewFoo<T: Foo>(T);
// Impl Deref and DerefMut to a Target which itself supports Foo
#[autoimpl(Deref<Target = dyn Foo>, DerefMut using self.0)]
pub struct ArcDynFoo(Arc<dyn Foo>);
#[test]
fn test_foo_newtypes() {
struct Success;
impl Foo for Success {
fn success(&self) -> bool { true }
}
// We can now directly call Foo's methods on the wrapper:
assert!(NewFoo(Success).success());
assert!(ArcDynFoo(Arc::new(Success)).success());
}
See tests/newtype.rs
for more variants of this pattern.
#[impl_default]
implements std::default::Default
:
#[impl_tools::impl_default(Tree::Ash)]
enum Tree { Ash, Beech, Birch, Willow }
impl_tools::impl_scope! {
#[impl_default]
struct Copse {
tree_type: Tree,
number: u32 = 7,
}
}
Note: #[impl_default]
is matched within an impl_scope!
regardless of imports.
impl_scope!
is a function-like macro used to define a type plus its
implementations. It supports impl Self
syntax:
use std::fmt::Display;
impl_tools::impl_scope! {
/// I don't know why this exists
pub struct NamedThing<T: Display, F> {
name: T,
func: F,
}
// Repeats generic parameters of type
impl Self {
fn format_name(&self) -> String {
format!("{}", self.name)
}
}
// Merges generic parameters of type
impl<O> Self where F: Fn(&str) -> O {
fn invoke(&self) -> O {
(self.func)(&self.format_name())
}
}
}
Caveat: rustfmt
won't currently touch the contents. Hopefully that
can be fixed!
impl_anon!
is a function-like macro to construct a single-use struct with
custom implementations (similar: RFC#2604).
Example:
use std::fmt;
fn main() {
let world = "world";
let says_hello_world = impl_tools::impl_anon! {
struct(&'static str = world);
impl fmt::Display for Self {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "hello {}", self.0)
}
}
};
assert_eq!(format!("{}", says_hello_world), "hello world");
}
Rust's #[derive]
macro is extensible via #[proc_macro_derive]
in a proc-macro
crate.
Our macros cannot be extended in the same way, but they can be extended via a new front-end:
impl-tools
crate to create a new "front-end" (proc-macro
crate).
This crate is contains only a little code over the impl-tools-lib
crate.#[autoimpl]
, write an impl of ImplTrait
and add it to the attribute's definition.
To extend impl_scope!
, write an impl of ScopeAttr
and add it to the macro's definition.impl-tools
.For an example of this approach, see kas-macros.
The MSRV is 1.61.0.
When using a sufficiently recent compiler version (presumably 1.65.0), generic associated types
are supported (only applicable to #[autoimpl]
on trait definitions using GATs).
Both Educe and Derivative
have similar functionality: the ability to implement standard traits with more flexibility than
libstd's #[derive]
.
In comparison, impl-tools' #[autoimpl]
has cleaner syntax but is less flexible:
#[derive(Derivative)]
#[derivative(PartialEq, Eq)]
struct Foo<S, T: ?Sized> {
foo: S,
#[derivative(PartialEq="ignore")]
bar: u8,
#[derivative(PartialEq(bound=""), Eq(bound=""))]
ptr: *const T,
}
#[derive(Educe)]
#[educe(PartialEq(bound = "S: PartialEq"), Eq(bound = "S: Eq"))]
struct Foo<S, T: ?Sized> {
foo: S,
#[educe(PartialEq(ignore))]
bar: u8,
ptr: *const T,
}
// impl-tools:
#[autoimpl(PartialEq, Eq ignore self.bar where S: trait)]
struct Foo<S, T: ?Sized> {
foo: S,
bar: u8,
ptr: *const T,
}
Note: #[derive]
and Derivative
add bounds like S: PartialEq, T: PartialEq
on generic parameters by default; Educe
and impl-tools
do not.
derive_more isn't exactly an "alternative", simply
supporting #[derive]
for more standard traits such as Add
and From
.
This is not (currently) supported by #[autoimpl]
(or, to my knowledge, any alternative).
auto_impl allows implementing a trait for reference types
(&
, &mut
, Box
, Rc
, Arc
) as well as function types. The former (reference types) is
supported by #[autoimpl]
(and is slightly more general):
// auto_impl:
#[auto_impl(&, Box)]
trait Foo {
fn foo(&self);
}
// impl-tools:
#[autoimpl(for<T: trait + ?Sized> &T, Box<T>)]
trait Foo {
fn foo(&self);
}
The COPYRIGHT file includes a list of contributors who claim copyright on this project. This list may be incomplete; new contributors may optionally add themselves to this list.
The impl-tools library is published under the terms of the Apache License, Version 2.0. You may obtain a copy of this licence from the LICENSE file or on the following webpage: https://www.apache.org/licenses/LICENSE-2.0