How Sway Derives Auto-Implemented Traits Like Debug and AbiEncode
Sway generates Debug implementations through a Rust procedural macro that emits intermediate representation code, while AbiEncode relies on explicit hand-written trait implementations in the standard library without automatic derivation.
The FuelLabs/sway compiler uses two distinct architectural patterns to handle trait derivation. Understanding how auto-implemented traits like Debug and AbiEncode are derived in Sway is critical for debugging contract code and ensuring proper ABI serialization for the Fuel VM.
The Debug Derivation Path
Proc-Macro Architecture in the IR Layer
When you annotate a Sway struct with #[derive(Debug)], the compiler invokes a procedural macro defined in sway-ir/sway-ir-macros/src/lib.rs. This macro generates Rust implementations of the DebugWithContext trait for the compiler's intermediate representation (IR) types.
The trait definition lives in sway-ir/src/pretty.rs:
pub trait DebugWithContext {
fn fmt(&self, engines: &Engines) -> String;
}
The macro walks the Sway AST, builds a fmt method that recursively calls debug_with_context on each field, and injects the generated implementation into the IR. This allows the Sway debug! macro to produce formatted output at runtime.
Debug Code Example
use std::debug::debug;
#[derive(Debug)]
struct User {
id: u64,
name: str[20],
}
fn main() -> u64 {
let alice = User { id: 1, name: "Alice" };
debug(alice); // Prints: User { id: 1, name: "Alice" }
0
}
Behind the scenes, the procedural macro emits Rust code conceptually equivalent to:
impl DebugWithContext for User {
fn fmt(&self, engines: &Engines) -> String {
format!(
"User {{ id: {}, name: {} }}",
self.id.fmt(engines),
self.name.fmt(engines)
)
}
}
The AbiEncode Implementation Path
Standard Library Trait Definition
Unlike Debug, the AbiEncode trait is defined directly in Sway within sway-lib-std/src/codec.sw. There is no automatic derivation mechanism—only hand-written implementations for primitives and generic containers.
pub trait AbiEncode {
fn abi_encode(&self) -> RawSlice;
}
Hand-Written Implementations
The standard library provides explicit impl blocks for all built-in types. For generic containers like Vec<T>, the implementation requires T: AbiEncode and encodes the length prefix followed by each element:
impl<T> AbiEncode for Vec<T>
where
T: AbiEncode,
{
fn abi_encode(&self) -> RawSlice {
// Encode length + recursive element encoding
}
}
Compile-Time Size Analysis
The compiler validates ABI safety through abi_encode_size_hint() in sway-core/src/type_system/info.rs. This function inspects TypeInfo and returns size estimates categorized as:
- Exact(n) – Fixed-size types like
u64orbool - Range(min, max) – Dynamic types with known bounds
- PotentiallyInfinite – Unbounded collections that might overflow buffers
Manual Implementation Requirement
User-defined structs must implement AbiEncode manually because no derive macro exists:
struct Point {
x: u64,
y: u64,
}
impl AbiEncode for Point {
fn abi_encode(&self) -> raw_slice {
let mut buf = RawSlice::new(16);
buf.write_u64(self.x);
buf.write_u64(self.y);
buf
}
}
Summary
Debugderivation uses a Rust procedural macro insway-ir/sway-ir-macros/src/lib.rsto generateDebugWithContextimplementations for IR types, enabling#[derive(Debug)]in Sway source code.AbiEncodelacks automatic derivation; the trait is defined insway-lib-std/src/codec.swwith hand-written implementations for primitives, standard library types, and generic containers.- The compiler analyzes encoding sizes via
abi_encode_size_hint()insway-core/src/type_system/info.rsto ensure contract ABI compatibility at compile time. - Custom structs require explicit
impl AbiEncodeblocks, whileDebugworks automatically through the derive attribute.
Frequently Asked Questions
Can I derive AbiEncode automatically in Sway?
No. Sway does not provide a #[derive(AbiEncode)] macro. You must either use types that already implement the trait (primitives, Vec<T>, etc.) or provide a manual impl AbiEncode for YourType block in your code according to the FuelLabs/sway standard library patterns.
Where is the Debug derive macro defined?
The #[derive(Debug)] functionality is implemented as a procedural macro in sway-ir/sway-ir-macros/src/lib.rs. This macro generates implementations of the DebugWithContext trait defined in sway-ir/src/pretty.rs, which operates on the compiler's intermediate representation rather than Sway source code directly.
Why do I need to implement AbiEncode manually for custom structs?
The Sway compiler does not generate AbiEncode implementations automatically because ABI encoding requires explicit control over binary layout for Fuel VM contract interactions. The trait is defined in sway-lib-std/src/codec.sw with hand-written implementations to ensure predictable, gas-efficient serialization for blockchain transactions.
How does the compiler estimate ABI encoding size?
The size estimation logic resides in sway-core/src/type_system/info.rs within the abi_encode_size_hint() function. It inspects TypeInfo to return size categories like Exact(n) for fixed-size types, Range(min, max) for dynamic types with bounds, or PotentiallyInfinite for unbounded collections, enabling compile-time validation of contract call buffer sizes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →