Crate synbounds

Crate synbounds 

Source
Expand description

Helper methods and macros for analyzing and manipulating generic bounds in Rust proc-macro crates.

This crate provides utilities built on top of the syn crate to track which generic parameters are referenced in a syntax tree, extract bounded generics, and manipulate lifetime parameters.

§Overview

The primary type is BoundGenerics, which implements the syn::visit::Visit trait to track which generic parameters (types, lifetimes, and consts) are referenced in an AST subtree. This is particularly useful when writing derive macros or other procedural macros that need to:

  • Determine which generic parameters are actually used in a struct or enum
  • Extract only the relevant where clauses for used parameters
  • Create minimal generic bounds for generated implementations

§Examples

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();

§Analyzing Impl Blocks

use syn::{parse_quote, ItemImpl};
use syn::visit::Visit;
use synbounds::BoundGenerics;

// Parse an impl block
let item: ItemImpl = parse_quote! {
    impl<T, U, V> MyStruct<T> {
        fn process(&self, value: U) -> String {
            unimplemented!()
        }
    }
};

// Track which generics are used in method signatures
let mut bounds = BoundGenerics::new(&item.generics);
bounds.visit_type(&item.self_ty); // Visit self type
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();

§Substituting Lifetimes

The crate also provides utilities for replacing lifetimes in syntax trees (requires the substitute feature, enabled by default):

use syn::{parse_quote, Type};
use syn::visit_mut::VisitMut;
use synbounds::substitute_with_static_lifetime;

// Replace all lifetimes with 'static
let mut ty: Type = parse_quote! { &'a Vec<&'b str> };
let mut visitor = substitute_with_static_lifetime();
visitor.visit_type_mut(&mut ty);
// ty is now: &'static Vec<&'static str>

You can also substitute with a specific lifetime using substitute_with_lifetime:

use syn::{parse_quote, Type, Lifetime};
use syn::visit_mut::VisitMut;
use synbounds::substitute_with_lifetime;

let target_lifetime: Lifetime = parse_quote! { 'target };
let mut ty: Type = parse_quote! { &'a String };
let mut visitor = substitute_with_lifetime(&target_lifetime);
visitor.visit_type_mut(&mut ty);
// ty is now: &'target String

Or create custom substitution logic with SubstituteLifetimes:

use syn::{parse_quote, Type, Lifetime};
use syn::visit_mut::VisitMut;
use synbounds::SubstituteLifetimes;

let mut ty: Type = parse_quote! { &'a String };
let mut visitor = SubstituteLifetimes(|lifetime: &mut Lifetime| {
    if lifetime.ident == "a" {
        *lifetime = Lifetime::new("'long", lifetime.span());
    }
});
visitor.visit_type_mut(&mut ty);
// ty is now: &'long String

§Substituting Self Types

When generating wrapper types or helper functions, you often need to replace Self with the concrete type. The SubstituteSelfType visitor handles this:

use 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:

use syn::{parse_quote, Signature, Type};
use syn::visit_mut::VisitMut;
use synbounds::SubstituteSelfType;

// Original method signature with Self
let mut sig: Signature = parse_quote! {
    fn process(input: &Self) -> Option<Self>
};

let concrete: Type = parse_quote! { MyStruct<'a, T> };
let mut visitor = SubstituteSelfType::new(&concrete);
visitor.visit_signature_mut(&mut sig);
// Signature now uses: MyStruct<'a, T> instead of Self

§Features

  • proc-macro (default): Enable proc-macro support
  • full (optional): Enable full syn feature for additional syntax support
  • substitute (default): Enable lifetime substitution utilities

Structs§

BoundGenerics
A syn::visit::Visit that tracks which generic parameters are bound.
ContainsBoundGenerics
A syn::visit::Visit that tests whether any of the given generic parameters are contained and whether they are bound or unbound.
SubstituteLifetimessubstitute
A syn::visit_mut::VisitMut that substitutes lifetimes according to the provided function.
SubstituteSelfTypesubstitute
A syn::visit_mut::VisitMut that substitutes the Self type with a concrete type.

Enums§

GenericParamRef
A reference to a generic parameter: type, lifetime, or const.

Functions§

substitute_self_typesubstitute
Creates a SubstituteSelfType visitor that substitutes Self types with the provided concrete type.
substitute_with_lifetimesubstitute
Creates a SubstituteLifetimes visitor that substitutes all non-'static lifetimes with the provided lifetime.
substitute_with_static_lifetimesubstitute
Creates a SubstituteLifetimes visitor that substitutes all non-'static lifetimes with 'static.