Methods with #[arcane]
Using #[arcane] on methods requires special handling because of how the macro transforms the function body.
The Problem
#![allow(unused)] fn main() { impl MyType { // This won't work as expected! #[arcane] fn process(&self, token: Desktop64) -> f32 { self.data[0] // Error: `self` not available in inner function } } }
The macro generates an inner function where self becomes a regular parameter.
The Solution: _self = Type
Use the _self argument and reference _self in your code:
#![allow(unused)] fn main() { use archmage::{Desktop64, arcane}; use magetypes::simd::f32x8; struct Vector8([f32; 8]); impl Vector8 { #[arcane(_self = Vector8)] fn magnitude(&self, token: Desktop64) -> f32 { // Use _self, not self let v = f32x8::from_array(token, _self.0); (v * v).reduce_add().sqrt() } } }
All Receiver Types
&self (Shared Reference)
#![allow(unused)] fn main() { impl Vector8 { #[arcane(_self = Vector8)] fn dot(&self, token: Desktop64, other: &Self) -> f32 { let a = f32x8::from_array(token, _self.0); let b = f32x8::from_array(token, other.0); (a * b).reduce_add() } } }
&mut self (Mutable Reference)
#![allow(unused)] fn main() { impl Vector8 { #[arcane(_self = Vector8)] fn normalize(&mut self, token: Desktop64) { let v = f32x8::from_array(token, _self.0); let len = (v * v).reduce_add().sqrt(); if len > 0.0 { let inv = f32x8::splat(token, 1.0 / len); let normalized = v * inv; _self.0 = normalized.to_array(); } } } }
self (By Value)
#![allow(unused)] fn main() { impl Vector8 { #[arcane(_self = Vector8)] fn scaled(self, token: Desktop64, factor: f32) -> Self { let v = f32x8::from_array(token, _self.0); let s = f32x8::splat(token, factor); Vector8((v * s).to_array()) } } }
Trait Implementations
Works with traits too:
#![allow(unused)] fn main() { trait SimdOps { fn double(&self, token: Desktop64) -> Self; } impl SimdOps for Vector8 { #[arcane(_self = Vector8)] fn double(&self, token: Desktop64) -> Self { let v = f32x8::from_array(token, _self.0); Vector8((v + v).to_array()) } } }
Why _self?
The name _self reminds you that:
- You're not using the normal
selfkeyword - The macro has transformed the function
- You need to be explicit about the type
It's a deliberate choice to make the transformation visible.
Generated Code
#![allow(unused)] fn main() { // You write: impl Vector8 { #[arcane(_self = Vector8)] fn process(&self, token: Desktop64) -> f32 { f32x8::from_array(token, _self.0).reduce_add() } } // Macro generates: impl Vector8 { fn process(&self, token: Desktop64) -> f32 { #[target_feature(enable = "avx2,fma,bmi1,bmi2")] #[inline] unsafe fn __inner(_self: &Vector8, token: Desktop64) -> f32 { f32x8::from_array(token, _self.0).reduce_add() } unsafe { __inner(self, token) } } } }
Common Patterns
Builder Pattern
#![allow(unused)] fn main() { impl ImageProcessor { #[arcane(_self = ImageProcessor)] fn with_brightness(self, token: Desktop64, amount: f32) -> Self { let mut result = _self; // Process brightness... result } #[arcane(_self = ImageProcessor)] fn with_contrast(self, token: Desktop64, amount: f32) -> Self { let mut result = _self; // Process contrast... result } } // Usage let processed = processor .with_brightness(token, 1.2) .with_contrast(token, 1.1); }
Mutable Iteration
#![allow(unused)] fn main() { impl Buffer { #[arcane(_self = Buffer)] fn process_all(&mut self, token: Desktop64) { for chunk in _self.data.chunks_exact_mut(8) { let v = f32x8::from_slice(token, chunk); let result = v * v; result.store_slice(chunk); } } } }