wtf-string
OsString-shaped strings with native, conversion-free u16 (WTF-16) storage.
Rust's OsString stores WTF-8 on Windows, so every wide (*W) Win32 call pays a
WTF-8 ↔ UTF-16 re-encode and an allocation. wtf-string stores [u16] natively
(WTF-16: arbitrary, ill-formed-surrogate-tolerant UTF-16), so the conversion
happens once at the str/OsStr boundary and Windows calls are fed with no
conversion and no per-call allocation.
- Encoding-generic core —
WtfString<E>/WtfStr<E>; ships both theWtf16arm (Wtf16String/Wtf16Str) and theWtf8arm (Wtf8String/Wtf8Str), au8/WTF-8 storage variant matchingOsString's WTF-8 layout. - Always-terminated storage — a hidden trailing NUL makes
LPCWSTRreturn allocation-free, while content spans exclude only that terminator (interior NULs in content are still preserved). - Portable core — storage and
str/Stringconversions work everywhere; only theOsStr/OsStringinterop is Windows-only. The crate isno_stdat its root: turn off the defaultstdfeature and the whole core, including the FFI pointer surface, still builds onallocalone. - Optional
windowsinterop — the off-by-defaultwindows-corefeature implementsParam<PCWSTR>for&Wtf16String, so high-levelwindowsAPIs accept it directly with no conversion. Without it, the crate has zero dependencies.
use Wtf16String;
// Encode once, at the boundary.
let s = from;
// From here on the units are the storage: no re-encode, no allocation.
assert_eq!;
assert_eq!;
Conversion costs
The point of the crate is that the middle rows are free; everything that costs anything is a boundary crossing you asked for.
| Operation | Cost |
|---|---|
Wtf16String::from(&str) / from(String) |
encode + allocate, once |
Wtf16String::from_os_str (Windows) |
encode + allocate, once |
as_ptr() + len(), for a counted *W call |
free |
as_terminated_ptr(), for an LPCWSTR call |
free |
encode_wide() (Windows), the OsStrExt analog |
free (borrows our slice) |
as_units() / len() / comparison / hashing |
free |
to_string_checked() / to_string_lossy() |
decode + allocate |
to_os_string() (Windows) |
re-encode + allocate |
OsString is the mirror image: its first rows are free and the wide-call rows
re-encode. Which type is right depends on whether your code spends its time in
str or in Win32.
FFI surface
Both directions of a Win32 call are covered without leaving u16:
- Input —
as_ptr()+len()for counted parameters, oras_terminated_ptr()forLPCWSTR/PCWSTR. The terminator is always present in the owned buffer, so the terminated form never allocates. - Output —
with_capacity()/as_mut_ptr()/set_len_from_ffi()for caller-allocated buffer-fill APIs, orfrom_wide_ptr()to copy out of a callee-allocated buffer.
examples/win32_round_trip.rs exercises all
three against real kernel32 entry points:
Interior NULs
Content may contain NUL, matching OsString. The trailing terminator is
storage, not content: it is excluded from len(), as_units(), comparison and
hashing. Because a C string ends at the first NUL, a callee reading
as_terminated_ptr() sees a value with an interior NUL truncated — counted
access is always exact, and has_interior_nul() reports the condition. A
checked no-interior-NUL companion type is a reserved seam for a future release.
Status: the v1 surface is complete and publication-ready; not yet released. See CHECKLIST.md and DESIGN-NOTES.md.
License
MIT. Copyright (c) Mike Grier.