# Sigil coercion and `EncodeAs`
An `enc` path (`#[klv(enc = ...)]` on a field, or `enc = ...` inside a
container `default(...)` / `key(...)` / `len(...)` block) may carry a
leading sigil - `&` or `*` - that tells the macro how to shape the call
to the underlying encoder function. This page explains the three forms
and when to use each.
## Why sigils exist
The [`EncodeValue`](./traits.md) trait is defined as:
```rust,ignore
pub trait EncodeValue<O: EncodedOutput> {
fn encode_value(&self) -> O;
}
```
`&self` gives the derive macro access to each field by reference. So
when the macro emits the per-field encode call, it has `&field_value`
in hand.
But encoders come in three shapes:
1. **Borrowed-form encoders** already take a reference:
```rust,ignore
fn my_encoder(input: &InnerValue) -> Vec<u8> { ... }
```
`&field_value` matches `&InnerValue` directly.
2. **`EncodeAs`-coerced encoders** take a cheaply-borrowed "natural"
view of the value:
```rust,ignore
pub fn utf8(s: &str) -> Vec<u8> { ... } // String -> &str
pub fn slice(xs: &[u8]) -> Vec<u8> { ... } // Vec<u8> -> &[u8]
```
3. **Value-form encoders** - how all the built-in primitive encoders in
[`tinyklv::codecs::binary::enc`](./codecs.md) are written - take the
value by value:
```rust,ignore
pub fn u8(x: u8) -> Vec<u8> { ... }
pub fn be_u16(x: u16) -> Vec<u8> { ... }
```
Passing `&u8` to `fn u8(x: u8)` is a type error. We need a bridge.
The `&` and `*` sigils are those bridges.
## The three forms
| `enc = path` | `fn(&T) -> O` | `path(&self.field)` |
| `enc = &path` | `fn(T::Borrowed) -> O` | `path(EncodeAs::encode_as(&self.field))` |
| `enc = *path` | `fn(T) -> O` | `path(self.field)` |
### No sigil - direct borrow
Use when your encoder already takes `&T`. Every encoder generated by
`#[derive(Klv)]` falls in this bucket, because `EncodeValue::encode_value`
takes `&self`:
```rust,ignore
#[klv(
key = 0x04,
dec = GpsFix::decode_value,
enc = GpsFix::encode_value, // &GpsFix -> Vec<u8>, no sigil needed
)]
gps: GpsFix,
```
### With `&` sigil - `EncodeAs` bridge
Use when your encoder takes a borrowed view that differs from `&T`
(primitives by value, `String -> &str`, `Vec<T> -> &[T]`, smart pointers
to inner):
```rust,ignore
#[klv(key = 0x02, dec = decb::be_u16, enc = &encb::be_u16)]
temperature_centideg: u16,
```
Under the hood (simplified):
```rust,ignore
encb::be_u16(EncodeAs::encode_as(&self.temperature_centideg))
```
For primitives `EncodeAs::encode_as(&u16)` is `*self` - a `Copy` - so
this compiles down to a plain call with zero overhead.
### With `*` sigil - by-value for `Copy` types
Use when your encoder takes `T` by value and `T: Copy`. Emits
`path(self.field)` directly, without the `EncodeAs` hop:
```rust,ignore
#[klv(key = 0x01, dec = decb::u8, enc = *encb::u8)]
sequence: u8,
```
This is the shortest path for primitive `Copy` types paired with
value-taking encoders. Prefer `&` when in doubt - it works for
non-`Copy` owned types too.
## The `EncodeAs` trait
```rust,ignore
pub trait EncodeAs {
type Borrowed<'a> where Self: 'a;
fn encode_as(&self) -> Self::Borrowed<'_>;
}
```
Built-in implementations:
| `u8`..`u128`, `i8`..`i128`, `f32`, `f64`, `bool`, `char`, `usize`, `isize` | `&'a Self` | pass through |
| `String` | `&'a str` | borrow as `&str` |
| `Vec<T>` | `&'a [T]` | borrow as slice |
| `Cow<'_, str>` | `&'a str` | to `&str` |
| `Cow<'_, [T]>` | `&'a [T]` | to slice |
| `Box<T>`, `Rc<T>`, `Arc<T>` | `&'a T` | deref to inner |
Every conversion is zero-cost: no clones, no allocations.
## When to implement `EncodeAs` yourself
You don't need to - for custom structs, use the no-sigil form and point
`enc` at a function taking `&YourType`. The derive-generated
`YourType::encode_value` already has that signature.
Implement `EncodeAs` on your own type **only** when:
- You want your type to work with the `&` sigil form, AND
- You want the encoder to receive a borrowed view distinct from `&Self`
(e.g. a newtype over `String` that should coerce to `&str`).
This is rare.
## Summary
- `enc = path` - encoder takes `&T`, field is borrowed directly.
- `enc = &path` - encoder takes the `EncodeAs::Borrowed` view of `T`.
- `enc = *path` - encoder takes `T` by value (requires `T: Copy`).
- Use the no-sigil form for custom types whose encoders take `&Self`
(including all `#[derive(Klv)]`-generated `encode_value` methods).
- Use the `&` form for the built-in primitive encoders in
`tinyklv::codecs::binary::enc` and for owned containers like `String`
/ `Vec<T>`.
- Use the `*` form as a shortcut when both sides are primitive `Copy`
types.