impl_interface Macro
Relevant source files
The impl_interface
macro enables trait implementations to be used across crate boundaries by generating exported extern functions that can be linked at runtime. This macro transforms a standard trait implementation into a cross-crate compatible implementation by creating symbol exports that follow the interface contract established by def_interface
.
For information about defining interfaces, see def_interface Macro. For information about calling cross-crate interfaces, see call_interface Macro.
Purpose and Functionality
The impl_interface
macro serves as the implementation phase of the three-macro system. It takes a trait implementation and automatically generates the necessary extern function exports that can be linked to by other crates, eliminating the need for direct crate dependencies while maintaining type safety.
Core Functions:
- Parses trait implementations and extracts method signatures
- Generates
#[export_name]
extern functions for each trait method - Maintains original implementation logic while adding cross-crate compatibility
- Handles both instance methods (
&self
) and static methods
Sources: src/lib.rs(L77 - L86) README.md(L20 - L28)
Syntax and Usage
The macro is applied as an attribute to trait implementations:
#[impl_interface]
impl TraitName for StructName {
// method implementations
}
The macro requires:
- A trait implementation (not a standalone impl block)
- The trait must be previously defined with
#[def_interface]
- The implementation struct must be a named type (not anonymous)
Usage Example from Tests:
#![allow(unused)] fn main() { #[impl_interface] impl SimpleIf for SimpleIfImpl { fn foo() -> u32 { 456 } fn bar(&self, a: u16, b: &[u8], c: &str) { /* implementation */ } } }
Sources: tests/test_crate_interface.rs(L15 - L27) src/lib.rs(L87 - L94)
Code Generation Process
Implementation Analysis Flow
flowchart TD A["Parse ItemImpl AST"] B["Extract trait_name"] C["Extract impl_name (struct)"] D["Iterate over impl methods"] E["For each ImplItem::Fn"] F["Generate extern function signature"] G["Create export_name attribute"] H["Generate method wrapper"] I["Insert into original method"] J["Return modified implementation"] A --> B B --> C C --> D D --> E D --> J E --> F F --> G G --> H H --> I I --> D
The macro performs the following transformations:
- Parse Implementation: Extracts trait name and implementing struct name from the AST
- Method Processing: For each method in the implementation, generates corresponding extern functions
- Signature Transformation: Removes
self
parameter from extern function signatures while preserving other parameters - Export Generation: Creates
#[export_name]
attributes following the__TraitName_methodName
convention - Wrapper Injection: Injects the extern function into the original method implementation
Sources: src/lib.rs(L96 - L107) src/lib.rs(L108 - L161)
Generated Code Structure
flowchart TD subgraph subGraph0["Generated Components"] D["Original method body"] E["Inline extern function"] F["#[export_name] attribute"] G["Symbol export"] end A["Original Implementation"] B["impl_interface macro"] C["Modified Implementation"] A --> B B --> C C --> D C --> E E --> F F --> G
Sources: src/lib.rs(L140 - L157) README.md(L62 - L78)
Method Signature Processing
The macro handles two types of method signatures differently:
Instance Methods (with&self)
For methods that take &self
as the first parameter:
- Extern Function: Removes
&self
from the signature - Call Generation: Creates an instance of the implementing struct and calls the method on it
- Parameter Forwarding: Passes all non-self parameters to the method call
Generated Pattern:
#[export_name = "__TraitName_methodName"]
extern "Rust" fn __TraitName_methodName(/* params without self */) -> ReturnType {
let _impl: StructName = StructName;
_impl.methodName(/* params */)
}
Static Methods (no&self)
For static methods that don't take &self
:
- Direct Call: Calls the static method directly on the implementing type
- Parameter Preservation: Maintains all original parameters in the extern function
Generated Pattern:
#![allow(unused)] fn main() { #[export_name = "__TraitName_methodName"] extern "Rust" fn __TraitName_methodName(/* all params */) -> ReturnType { StructName::methodName(/* params */) } }
Sources: src/lib.rs(L119 - L138) tests/test_crate_interface.rs(L18 - L26)
Symbol Export Mechanism
Export Name Generation
flowchart TD A["trait_name: SimpleIf"] C["Combine with separator"] B["method_name: bar"] D["__SimpleIf_bar"] E["#[export_name] attribute"] F["Linker symbol export"] G["External crate"] H["Symbol resolution"] I["Runtime function call"] A --> C B --> C C --> D D --> E E --> F F --> H G --> H H --> I
The naming convention __TraitName_methodName
ensures:
- Uniqueness: Prevents symbol collisions across different traits
- Consistency: Matches the naming pattern expected by
def_interface
- Linkability: Creates symbols that the Rust linker can resolve across crates
Sources: src/lib.rs(L113 - L116) src/lib.rs(L148)
Integration with Macro System
Cross-Macro Coordination
flowchart TD subgraph subGraph2["call_interface Usage"] D["unsafe call to extern fn"] end subgraph subGraph1["impl_interface Output"] B["#[export_name = '__TraitName_method']"] C["extern fn implementation"] end subgraph subGraph0["def_interface Output"] A["extern fn __TraitName_method"] end E["Linker resolution"] A --> B C --> E D --> E
The impl_interface
macro must coordinate with the other macros:
- Symbol Contract: Must export functions with names matching
def_interface
declarations - Type Compatibility: Generated extern functions must match the signatures expected by
call_interface
- Linking Requirements: Exported symbols must be available at link time for consuming crates
Sources: src/lib.rs(L203 - L209) README.md(L52 - L58)
Error Handling and Validation
The macro performs several validation checks:
Input Validation:
- Ensures the attribute has no parameters:
#[impl_interface]
only - Verifies the target is a trait implementation, not a standalone impl block
- Requires the implementing type to be a named struct (not anonymous)
Error Cases:
// Invalid: attribute parameters
#[impl_interface(param)] // Compile error
// Invalid: not a trait implementation
#[impl_interface]
impl SomeStruct { } // Compile error
// Invalid: anonymous type
#[impl_interface]
impl SomeTrait for (u32, String) { } // Compile error
Sources: src/lib.rs(L88 - L94) src/lib.rs(L99 - L106)
Complete Transformation Example
Input Code:
#![allow(unused)] fn main() { #[impl_interface] impl SimpleIf for SimpleIfImpl { fn bar(&self, a: u16, b: &[u8], c: &str) { println!("{} {:?} {}", a, b, c); } } }
Generated Output (Conceptual):
impl SimpleIf for SimpleIfImpl {
#[inline]
fn bar(&self, a: u16, b: &[u8], c: &str) {
{
#[inline]
#[export_name = "__SimpleIf_bar"]
extern "Rust" fn __SimpleIf_bar(a: u16, b: &[u8], c: &str) {
let _impl: SimpleIfImpl = SimpleIfImpl;
_impl.bar(a, b, c)
}
}
{
println!("{} {:?} {}", a, b, c);
}
}
}
This transformation enables the implementation to be called from any crate that links to this one, without requiring direct dependency relationships.
Sources: README.md(L62 - L78) tests/test_crate_interface.rs(L23 - L26)