| Crates.io | synbounds |
| lib.rs | synbounds |
| version | 0.1.3 |
| created_at | 2025-11-01 15:45:12.959061+00 |
| updated_at | 2025-11-02 22:06:20.540899+00 |
| description | Helper methods and macros for custom derives |
| homepage | |
| repository | https://github.com/HellButcher/synbounds.git |
| max_upload_size | |
| id | 1912104 |
| size | 78,994 |
synboundssynbounds is a Rust crate that provides utilities for writing proc-macro crates that need to manipulate or analyze Rust type bounds.
It leverages the syn crate to parse and work with Rust syntax trees, making it easier to handle complex type bounds in procedural macros.
where clauses containing only the predicates for used genericsSelf with concrete types in method signatures and return typesAdd this to your Cargo.toml:
[dependencies]
synbounds = "0.1"
use syn::{parse_quote, DeriveInput};
use syn::visit::Visit;
use synbounds::BoundGenerics;
// Parse a struct definition
let input: DeriveInput = parse_quote! {
struct MyStruct<'a, T, U> {
field: &'a T,
}
};
// Track which generics are used in the fields
let mut bounds = BoundGenerics::new(&input.generics);
if let syn::Data::Struct(data) = &input.data {
bounds.visit_fields(&data.fields);
}
// Only T and 'a are used, U is not
let bound_generics = bounds.to_bound_generics();
// bound_generics is now: <'a, T>
use syn::{parse_quote, DeriveInput};
use syn::visit::Visit;
use synbounds::BoundGenerics;
let input: DeriveInput = parse_quote! {
struct MyStruct<T, U>
where
T: Clone,
U: Default,
{
field: T,
}
};
let mut bounds = BoundGenerics::new(&input.generics);
if let syn::Data::Struct(data) = &input.data {
bounds.visit_fields(&data.fields);
}
// Only get where predicates for used generics (T)
let where_predicates: Vec<_> = bounds.bound_where_predicates().collect();
// where_predicates contains only: T: Clone
use syn::{parse_quote, ItemImpl};
use syn::visit::Visit;
use synbounds::BoundGenerics;
let item: ItemImpl = parse_quote! {
impl<T, U, V> MyStruct<T> {
fn process(&self, value: U) -> String {
unimplemented!()
}
}
};
// Track which generics are used
let mut bounds = BoundGenerics::new(&item.generics);
bounds.visit_type(&item.self_ty);
for impl_item in &item.items {
if let syn::ImplItem::Fn(method) = impl_item {
bounds.visit_signature(&method.sig);
}
}
// T and U are used, V is not
let bound_generics = bounds.to_bound_generics();
use syn::{parse_quote, Type};
use syn::visit_mut::VisitMut;
use synbounds::substitute_with_static_lifetime;
let mut ty: Type = parse_quote! { &'a String };
let mut visitor = substitute_with_static_lifetime::<()>();
visitor.visit_type_mut(&mut ty);
// ty is now: &'static String
Self Typesuse syn::{parse_quote, Type};
use syn::visit_mut::VisitMut;
use synbounds::SubstituteSelfType;
let concrete_type: Type = parse_quote! { MyStruct<T, U> };
let mut return_type: Type = parse_quote! { Result<Self, Error> };
let mut visitor = SubstituteSelfType::new(&concrete_type);
visitor.visit_type_mut(&mut return_type);
// return_type is now: Result<MyStruct<T, U>, Error>
This is especially useful when defining private wrapper objects for functions that use Self.
proc-macro (default): Enable proc-macro supportfull: Enable full syn feature for additional syntax supportsubstitute (default): Enable lifetime substitution utilitiesThis crate is particularly useful when:
When writing a proc-macro for a specific library, I came across the problem, that I needed to know
which generic bounds where actually used on a specific part of the AST (Functions in impl blocks
to be precise).
While syn provides the necessary structures to parse and represent these bounds, it does not
provide utilities to easily extract or manipulate them. So I first wrote some helper functions for
my specific use case.
Later I came across the synstructure crate, which provided some similar functionality, but was
more focused on deriving implementations for enums and structs. But this inspired me to generalize
and refactor my helper functions into a standalone crate that could be reused in other
proc-macro projects.
This project is licensed under either of
at your option
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.