# Owned vs Borrowed Types
Several ASN.1 string and binary types have two Rust representations in synta:
| `OCTET STRING` | `OctetString` | `OctetStringRef<'a>` |
| `BIT STRING` | `BitString` | `BitStringRef<'a>` |
| `UTF8String` | `Utf8String` | `Utf8StringRef<'a>` |
| `PrintableString` | `PrintableString` | `PrintableStringRef<'a>` |
| `IA5String` | `IA5String` | `IA5StringRef<'a>` |
`CodeGenConfig::string_type_mode` selects which form synta-codegen emits.
## Default: owned types
`StringTypeMode::Owned` (the default) emits the owned forms. Owned types
heap-allocate their contents on decode and have no lifetime parameter. This is
the most convenient choice when you need to construct structs programmatically
(e.g., in tests or message builders).
```asn1
Msg ::= SEQUENCE {
label UTF8String,
data OCTET STRING
}
```
Generated Rust (default):
```rust
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "derive", derive(Asn1Sequence))]
pub struct Msg {
pub label: Utf8String,
pub data: OctetString,
}
```
## Borrowed mode: zero-copy Ref types
`StringTypeMode::Borrowed` emits the `Ref` forms that borrow directly from the
decoder's input buffer. No heap allocation occurs for the string content at
decode time. This is optimal for parse-only workloads such as X.509 certificate
inspection, where structs are decoded, inspected, and discarded — never
constructed from scratch.
```rust
use synta_codegen::{CodeGenConfig, StringTypeMode};
let config = CodeGenConfig {
string_type_mode: StringTypeMode::Borrowed,
..Default::default()
};
let code = generate_with_config(&module, config)?;
```
Generated Rust (borrowed mode):
```rust
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "derive", derive(Asn1Sequence))]
pub struct Msg<'a> {
pub label: Utf8StringRef<'a>,
pub data: OctetStringRef<'a>,
}
```
## Which types are affected
Only the five types listed in the table above are affected. Other string types
— `TeletexString`, `UniversalString`, `BmpString`, `GeneralString`,
`NumericString`, `VisibleString` — have no `Ref` variant in synta and are always
emitted as owned types regardless of the mode.
Type aliases inherit the mode:
```asn1
MyLabel ::= UTF8String
```
Borrowed mode emits:
```rust
pub type MyLabel<'a> = Utf8StringRef<'a>;
```
## Lifetime propagation
When a struct contains a field of a borrowed type (directly or transitively),
synta-codegen adds a `'a` lifetime parameter to that struct. Structs that only
contain owned types or unaffected types receive no lifetime parameter.
```asn1
Inner ::= SEQUENCE { name UTF8String }
Outer ::= SEQUENCE { inner Inner, count INTEGER }
```
Borrowed mode:
```rust
pub struct Inner<'a> { pub name: Utf8StringRef<'a> }
pub struct Outer<'a> { pub inner: Inner<'a>, pub count: Integer }
```
`Outer` gains `<'a>` because it contains `Inner<'a>`.
## Named bit strings are always owned
A `BIT STRING` with named bits (a named-bit list) is always emitted as an owned
`BitString` regardless of mode, because it is decoded into a concrete bit-field
type and the bit constants are expressed as plain `u32` offsets into it.
```asn1
KeyUsage ::= BIT STRING {
digitalSignature (0),
keyEncipherment (2)
} (SIZE (32..MAX))
```
Generated Rust (both modes):
```rust
pub struct KeyUsage(pub synta::BitString); // always owned
impl KeyUsage {
pub const DIGITAL_SIGNATURE: u32 = 0;
pub const KEY_ENCIPHERMENT: u32 = 2;
}
```
## Choosing a mode
| Parse-only / inspection workload | `Borrowed` — zero allocation for string content |
| Building or mutating structs | `Owned` — no lifetime parameter to manage |
| Mixed (parse + build) | `Owned` (simpler) or split into separate types |