# Migrating from libtasn1
Guide for Rust developers moving from libtasn1 (via `libtasn1-sys` or a custom
FFI wrapper) to the synta crate.
## Why migrate
| Schema | Required at runtime | Not required; codegen at build time |
| API style | Tree-based path strings | Streaming decoder / encoder |
| Memory safety | Manual (`unsafe` in every call) | Pure Rust; no `unsafe` in hot paths |
| Error codes | C integer constants | `synta::Error` enum |
| Type system | Stringly typed paths | Strongly typed generated structs |
| Threading | Not thread-safe | Thread-safe (separate instances) |
| no_std | Not supported | Supported |
## Initialization comparison
**libtasn1 (Rust FFI)**
```rust,ignore
use libtasn1_sys::*;
use std::ptr;
let mut definitions: asn1_node = ptr::null_mut();
let rc = unsafe {
asn1_array2tree(pkix_asn1_tab.as_ptr(), &mut definitions, ptr::null_mut())
};
if rc != ASN1_SUCCESS {
return Err(...);
}
```
**Synta**
```rust,ignore
use synta::{Decoder, Encoding};
// No schema needed — create a decoder directly from bytes
let mut dec = Decoder::new(data, Encoding::Der);
```
## Decoding comparison
**libtasn1 (Rust FFI)**
```rust,ignore
let mut cert: asn1_node = ptr::null_mut();
unsafe {
asn1_create_element(definitions, b"PKIX1.Certificate\0".as_ptr() as _, &mut cert);
asn1_der_decoding(&mut cert, der.as_ptr(), der.len() as _, ptr::null_mut());
}
let field = b"tbsCertificate.serialNumber\0";
let mut len = 0i32;
unsafe {
asn1_read_value(cert, field.as_ptr() as _, ptr::null_mut(), &mut len);
}
let mut buf = vec![0u8; len as usize];
unsafe {
asn1_read_value(cert, field.as_ptr() as _, buf.as_mut_ptr() as _, &mut len);
asn1_delete_structure(&mut cert);
}
```
**Synta**
```rust,ignore
use synta::{Decoder, Encoding, Integer};
let mut dec = Decoder::new(der, Encoding::Der);
// With generated types from synta-certificate:
use synta_certificate::Certificate;
let cert: Certificate = dec.decode()?;
let serial = &cert.tbs_certificate.serial_number;
```
Or with the `synta-codegen` code-generation pipeline, define your ASN.1 schema and
get compile-time-typed structs — no path strings at all.
## Encoding comparison
**libtasn1 (Rust FFI)**
```rust,ignore
let mut alg_id: asn1_node = ptr::null_mut();
unsafe {
asn1_create_element(definitions, b"PKIX1.AlgorithmIdentifier\0".as_ptr() as _, &mut alg_id);
asn1_write_value(alg_id, b"algorithm\0".as_ptr() as _,
b"1.2.840.113549.1.1.1\0".as_ptr() as _, 1);
asn1_write_value(alg_id, b"parameters\0".as_ptr() as _, ptr::null(), 0);
}
let mut der_len = 0i32;
unsafe { asn1_der_coding(alg_id, b"\0".as_ptr() as _, ptr::null_mut(), &mut der_len, ptr::null_mut()); }
let mut der = vec![0u8; der_len as usize];
unsafe {
asn1_der_coding(alg_id, b"\0".as_ptr() as _, der.as_mut_ptr(), &mut der_len, ptr::null_mut());
asn1_delete_structure(&mut alg_id);
}
```
**Synta**
```rust,ignore
use synta::{Encoder, Encoding, ObjectIdentifier, Null};
use synta::types::constructed::{Element, Sequence};
let mut enc = Encoder::new(Encoding::Der);
// Encode SEQUENCE { OID, NULL }
let mut seq = Sequence::new();
let oid = ObjectIdentifier::new(&[1,2,840,113549,1,1,1]).unwrap();
seq.push(Element::ObjectIdentifier(oid));
seq.push(Element::Null(Null));
enc.encode(&seq)?;
let der = enc.finish()?;
```
## Iterating SEQUENCE OF
**libtasn1 (Rust FFI)**
```rust,ignore
let mut i = 1u32;
loop {
let path = format!("?{}\0", i);
let mut len = 0i32;
let rc = unsafe {
asn1_read_value(seq_of, path.as_ptr() as _, ptr::null_mut(), &mut len)
};
if rc == ASN1_ELEMENT_NOT_FOUND { break; }
let mut buf = vec![0u8; len as usize];
unsafe { asn1_read_value(seq_of, path.as_ptr() as _, buf.as_mut_ptr() as _, &mut len); }
// process buf
i += 1;
}
```
**Synta**
```rust,ignore
use synta::{Decoder, Encoding, Integer};
let mut dec = Decoder::new(der, Encoding::Der);
let items: Vec<Integer> = dec.decode()?; // SEQUENCE OF INTEGER
for item in &items {
// process item
}
```
## Type mapping
| `ASN1_SUCCESS` / integer error codes | `Result<T, synta::Error>` |
| `asn1_read_value(node, "field", ...)` | Field access on generated struct |
| `asn1_write_value(node, "field", ...)` | Set field on struct literal |
| `asn1_der_decoding` | `Decoder::decode::<T>()` |
| `asn1_der_coding` | `Encoder::encode()` + `finish()` |
| `asn1_create_element` | `Decoder::new()` / struct literal |
| `asn1_delete_structure` | Dropped automatically by Rust |
| `asn1_strerror` | `format!("{}", err)` |
## Error handling
**libtasn1 (Rust FFI)**
```rust,ignore
let rc = unsafe { asn1_der_decoding(&mut node, ...) };
if rc != ASN1_SUCCESS {
let msg = unsafe {
std::ffi::CStr::from_ptr(asn1_strerror(rc)).to_str().unwrap()
};
eprintln!("Error: {}", msg);
}
```
**Synta**
```rust,ignore
match decoder.decode::<MyType>() {
Ok(val) => { /* use val */ }
Err(e) => eprintln!("Error: {}", e),
}
```
## Safety improvements
### No schema parsing surface
libtasn1 parses schemas at runtime from C tables. Malformed schemas or mismatches
between schema and data are runtime errors. Synta uses compile-time codegen:
schema errors are caught at `cargo build` time, not at runtime.
### No path-string type confusion
libtasn1 `asn1_read_value` takes a path string and writes to an untyped buffer —
the caller must know the correct type and buffer size. Synta returns concrete Rust
types; the compiler enforces type correctness.
### Automatic memory management
libtasn1 structures must be freed with `asn1_delete_structure`. Forgetting a call
leaks memory; calling twice is undefined behaviour. Synta types implement `Drop`
and are freed automatically when they go out of scope.
### Thread safety
libtasn1 operations on shared structures are not thread-safe. Synta Decoder and
Encoder instances are separate objects and can be used independently on different
threads with no synchronization.
## Benefits summary
1. **Zero `unsafe` for parsing** — all decode operations are safe Rust.
2. **Compile-time schema validation** — schema errors surface during `cargo build`.
3. **Strongly typed fields** — no path strings; use struct fields directly.
4. **Automatic resource cleanup** — no `asn1_delete_structure` calls needed.
5. **no_std support** — run on bare-metal or WebAssembly targets.