Getting Started

Relevant source files

This document provides a quick start guide for using the crate_interface crate to define trait interfaces that can be implemented and called across crate boundaries. It covers the basic three-step workflow of defining interfaces, implementing them, and calling interface methods.

For comprehensive macro syntax and options, see Macro Reference. For understanding the underlying implementation details, see Architecture and Internals.

Purpose and Scope

The crate_interface crate solves circular dependency problems by enabling trait definitions in one crate while allowing implementations and usage in separate crates. This guide demonstrates the essential usage patterns through practical examples using the three core macros: def_interface, impl_interface, and call_interface!.

Installation

Add crate_interface to your Cargo.toml dependencies:

[dependencies]
crate_interface = "0.1.4"

The crate supports no-std environments and requires Rust 1.57 or later.

Sources: Cargo.toml(L1 - L22) 

Three-Step Workflow

The crate_interface system follows a consistent three-step pattern that maps directly to the three procedural macros:

Basic Workflow Diagram

flowchart TD
A["Step 1: Define"]
B["Step 2: Implement"]
C["Step 3: Call"]
D["#[def_interface]trait HelloIf"]
E["#[impl_interface]impl HelloIf"]
F["call_interface!HelloIf::hello"]
G["Generates __HelloIf_modextern fn declarations"]
H["Generates #[export_name]extern fn definitions"]
I["Generates unsafe__HelloIf_mod calls"]

A --> B
A --> D
B --> C
B --> E
C --> F
D --> G
E --> H
F --> I

Sources: README.md(L13 - L40)  tests/test_crate_interface.rs(L3 - L11) 

Step 1: Define Interface

Use the #[def_interface] attribute macro to define a trait interface:

#![allow(unused)]
fn main() {
#[crate_interface::def_interface]
pub trait HelloIf {
    fn hello(&self, name: &str, id: usize) -> String;
}
}

This generates the trait plus a hidden module containing extern "Rust" function declarations.

Step 2: Implement Interface

Use the #[impl_interface] attribute macro to implement the interface:

#![allow(unused)]
fn main() {
struct HelloIfImpl;

#[crate_interface::impl_interface]
impl HelloIf for HelloIfImpl {
    fn hello(&self, name: &str, id: usize) -> String {
        format!("Hello, {} {}!", name, id)
    }
}
}

This generates #[export_name] extern functions that can be linked across crates.

Step 3: Call Interface Methods

Use the call_interface! function-like macro to safely call interface methods:

use crate_interface::call_interface;

// Method-style calling
let result = call_interface!(HelloIf::hello("world", 123));

// Function-style calling  
let result = call_interface!(HelloIf::hello, "rust", 456);

Sources: README.md(L13 - L40)  tests/test_crate_interface.rs(L36 - L41) 

Generated Code Structure

Understanding what code gets generated helps debug issues and optimize usage:

Code Generation Mapping

flowchart TD
subgraph subGraph1["Generated Symbols"]
    D["trait HelloIf(unchanged)"]
    E["__HelloIf_mod module"]
    F["__HelloIf_helloextern declaration"]
    G["__HelloIf_helloextern definition"]
    H["#[export_name]attribute"]
    I["unsafe __HelloIf_mod::__HelloIf_hello call"]
end
subgraph subGraph0["Input Code"]
    A["#[def_interface]trait HelloIf"]
    B["#[impl_interface]impl HelloIf for HelloIfImpl"]
    C["call_interface!HelloIf::hello"]
end

A --> D
A --> E
A --> F
B --> G
B --> H
C --> I
F --> G
I --> F

The def_interface macro generates a module named __HelloIf_mod containing extern function declarations like __HelloIf_hello. The impl_interface macro creates matching extern function definitions with #[export_name] attributes. The call_interface! macro generates safe wrappers around unsafe extern calls.

Sources: README.md(L44 - L85) 

Advanced Usage Patterns

Default Method Implementations

Traits can include default implementations that will be preserved:

#![allow(unused)]
fn main() {
#[def_interface]
trait SimpleIf {
    fn foo() -> u32 {
        123  // Default implementation
    }
    
    fn bar(&self, a: u16, b: &[u8], c: &str);
}
}

Cross-Module Calls

Interface calls work from any module scope by using appropriate path syntax:

mod private {
    pub fn test_call_in_mod() {
        crate::call_interface!(super::SimpleIf::bar(123, &[2, 3, 5, 7, 11], "test"));
        crate::call_interface!(crate::SimpleIf::foo,);
    }
}

Method Documentation

Documentation comments are preserved in the generated code:

#![allow(unused)]
fn main() {
#[def_interface]
trait SimpleIf {
    /// Test comments for method
    fn bar(&self, a: u16, b: &[u8], c: &str);
}

#[impl_interface]  
impl SimpleIf for SimpleIfImpl {
    /// Implementation-specific documentation
    fn bar(&self, a: u16, b: &[u8], c: &str) {
        // Implementation here
    }
}
}

Sources: tests/test_crate_interface.rs(L3 - L42) 

Cross-Crate Usage

CrateRoleRequired Items
Interface CrateDefines traits#[def_interface]trait definition
Implementation CrateProvides implementations#[impl_interface]impl block + struct
Consumer CrateCalls methodscall_interface!macro calls

The implementation crate and consumer crate can be the same, different, or have multiple implementations across multiple crates.

Next Steps

Sources: README.md(L1 - L85)  tests/test_crate_interface.rs(L1 - L42)  Cargo.toml(L1 - L22)