Strings in ZIG done right.
All string types share the same core logic, acting as simple containers. The real functionality resides in internal modules like
utils.chars
andutils.unicode
, ensuring efficiency, maintainability, and elegance.
Works seamlessly with various integer types, including
u8
,u16
,u32
,u64
, and beyond.
Properly handles Unicode, preserving character integrity, including complex grapheme clusters like emojis and modifiers.
Matches the speed of Zig’s standard library and outperforms competitors by 488x times in benchmarks.
Every function is rigorously tested, making the library safe, reliable, and ready for production.
Designed with efficiency in mind, avoiding unnecessary allocations while maintaining flexibility.
🔹 🛠 API Reference – Detailed documentation of available functions.
🔹 ⚡ Performance & Benchmarks – Speed comparisons with other implementations.
🔹 🌍 Comparisons – Detailed comparison with other string libraries.
This library provides several types, each with its own approach to handling strings in the ZIG language.
You can choose from the following types according to the intended purpose:
Type | Description |
---|---|
Viewer |
Immutable fixed-size string type that supports unicode. |
Buffer |
Mutable fixed-size string type that supports unicode. |
String |
Managed dynamic-size string type that supports unicode. |
uString |
Unmanaged dynamic-size string type that supports unicode. |
Internal utility modules for handling char arrays and Unicode operations.
Module | Description |
---|---|
chars |
Utility functions for char arrays. |
unicode |
Utility functions for Unicode codepoints and grapheme clusters. |
✔️ Implemented and tested.
⚒️ Under development.
🔩 Exists only for internal API integration.
❌ Contradicts container logic.
Function | Viewer | Buffer | String | uString |
---|---|---|---|---|
init | ✔️ | ✔️ | ✔️ | ✔️ |
initEmpty | ✔️ | ✔️ | ✔️ | ✔️ |
initWithSelf | ✔️ | ✔️ | ✔️ | ✔️ |
initWithSlice | ✔️ | ✔️ | ✔️ | ✔️ |
initWithChar | ❌ | ✔️ | ✔️ | ✔️ |
initWithCapacity | ❌ | ✔️ | ✔️ | ✔️ |
initWithFmt | ❌ | ✔️ | ✔️ | ✔️ |
initWithAllocator | ❌ | ❌ | ✔️ | 🔩 |
deinit | ❌ | ❌ | ✔️ | ✔️ |
size | ✔️ | ✔️ | ✔️ | ✔️ |
len | ✔️ | ✔️ | ✔️ | ✔️ |
vlen | ✔️ | ✔️ | ✔️ | ✔️ |
src | ✔️ | ✔️ | ✔️ | ✔️ |
sub | ✔️ | ✔️ | ✔️ | ✔️ |
cString | ❌ | ✔️ | ✔️ | ✔️ |
allocatedSlice | ❌ | ❌ | ✔️ | ✔️ |
iterator | ✔️ | ✔️ | ✔️ | ✔️ |
writer | ❌ | ✔️ | ✔️ | ✔️ |
charAt | ✔️ | ✔️ | ✔️ | ✔️ |
atVisual | ✔️ | ✔️ | ✔️ | ✔️ |
find | ✔️ | ✔️ | ✔️ | ✔️ |
findVisual | ✔️ | ✔️ | ✔️ | ✔️ |
findLast | ✔️ | ✔️ | ✔️ | ✔️ |
findLastVisual | ✔️ | ✔️ | ✔️ | ✔️ |
includes | ✔️ | ✔️ | ✔️ | ✔️ |
startsWith | ✔️ | ✔️ | ✔️ | ✔️ |
endsWith | ✔️ | ✔️ | ✔️ | ✔️ |
clone | ✔️ | ✔️ | ✔️ | ✔️ |
isEqual | ✔️ | ✔️ | ✔️ | ✔️ |
isEmpty | ✔️ | ✔️ | ✔️ | ✔️ |
insert | ❌ | ✔️ | ✔️ | ✔️ |
insertSlice | ❌ | ✔️ | ✔️ | ✔️ |
insertChar | ❌ | ✔️ | ✔️ | ✔️ |
insertSelf | ❌ | ✔️ | ✔️ | ✔️ |
insertFmt | ❌ | ✔️ | ✔️ | ✔️ |
visualInsert | ❌ | ✔️ | ✔️ | ✔️ |
visualInsertSlice | ❌ | ✔️ | ✔️ | ✔️ |
visualInsertChar | ❌ | ✔️ | ✔️ | ✔️ |
visualInsertSelf | ❌ | ✔️ | ✔️ | ✔️ |
visualInsertFmt | ❌ | ✔️ | ✔️ | ✔️ |
append | ❌ | ✔️ | ✔️ | ✔️ |
appendSlice | ❌ | ✔️ | ✔️ | ✔️ |
appendChar | ❌ | ✔️ | ✔️ | ✔️ |
appendSelf | ❌ | ✔️ | ✔️ | ✔️ |
appendFmt | ❌ | ✔️ | ✔️ | ✔️ |
prepend | ❌ | ✔️ | ✔️ | ✔️ |
prependSlice | ❌ | ✔️ | ✔️ | ✔️ |
prependChar | ❌ | ✔️ | ✔️ | ✔️ |
prependSelf | ❌ | ✔️ | ✔️ | ✔️ |
prependFmt | ❌ | ✔️ | ✔️ | ✔️ |
repeat | ❌ | ✔️ | ✔️ | ✔️ |
removeIndex | ❌ | ✔️ | ✔️ | ✔️ |
removeVisualIndex | ❌ | ✔️ | ✔️ | ✔️ |
removeRange | ❌ | ✔️ | ✔️ | ✔️ |
removeVisualRange | ❌ | ✔️ | ✔️ | ✔️ |
pop | ❌ | ✔️ | ✔️ | ✔️ |
shift | ❌ | ✔️ | ✔️ | ✔️ |
trim | ❌ | ✔️ | ✔️ | ✔️ |
trimStart | ❌ | ✔️ | ✔️ | ✔️ |
trimEnd | ❌ | ✔️ | ✔️ | ✔️ |
replaceRange | ❌ | ✔️ | ✔️ | ✔️ |
replaceVisualRange | ❌ | ✔️ | ✔️ | ✔️ |
replaceFirst | ❌ | ✔️ | ✔️ | ✔️ |
replaceFirstN | ❌ | ✔️ | ✔️ | ✔️ |
replaceLast | ❌ | ✔️ | ✔️ | ✔️ |
replaceLastN | ❌ | ✔️ | ✔️ | ✔️ |
replaceNth | ❌ | ✔️ | ✔️ | ✔️ |
replaceAll | ❌ | ✔️ | ✔️ | ✔️ |
toLower | ❌ | ✔️ | ✔️ | ✔️ |
toUpper | ❌ | ✔️ | ✔️ | ✔️ |
toTitle | ❌ | ✔️ | ✔️ | ✔️ |
reverse | ❌ | ✔️ | ✔️ | ✔️ |
split | ✔️ | ✔️ | ✔️ | ✔️ |
splitAll | ✔️ | ✔️ | ✔️ | ✔️ |
splitToSelf | ✔️ | ✔️ | ✔️ | ✔️ |
splitAllToSelf | ✔️ | ✔️ | ✔️ | ✔️ |
clear | ✔️ | ✔️ | ✔️ | ✔️ |
clearAndFree | ❌ | ❌ | ✔️ | ✔️ |
shrink | ❌ | ❌ | ✔️ | ✔️ |
shrinkAndFree | ❌ | ❌ | ✔️ | ✔️ |
resize | ❌ | ❌ | ✔️ | ✔️ |
fromOwnedSlice | ❌ | ❌ | ✔️ | ✔️ |
toOwnedSlice | ❌ | ❌ | ✔️ | ✔️ |
toManaged | ⚒️ | ⚒️ | ❌ | ✔️ |
toUnmanaged | ⚒️ | ⚒️ | ✔️ | ❌ |
toViewer | ❌ | ⚒️ | ✔️ | ✔️ |
toInteger | ⚒️ | ⚒️ | ⚒️ | ⚒️ |
toFloat | ⚒️ | ⚒️ | ⚒️ | ⚒️ |
✔️ | ✔️ | ✔️ | ✔️ | |
printTo | ✔️ | ✔️ | ✔️ | ✔️ |
printWithNewLine | ✔️ | ✔️ | ✔️ | ✔️ |
Same speed of the standard library.
Faster then the competitor by 488x times ⚡⚡.
std.ArrayList.appendSlice
Operation | Runs | Total Time | Time/Run (avg) |
---|---|---|---|
x1 |
65535 | 841.846ms | 12.845µs ± 18.981µs |
x10 |
65535 | 702.371ms | 10.717µs ± 16.316µs |
x100 |
32767 | 1.247s | 38.068µs ± 28.696µs |
x1000 |
4095 | 1.188s | 290.298µs ± 37.238µs |
@JakubSzark/zig-string.concat
Operation | Runs | Total Time | Time/Run (avg ± σ) |
---|---|---|---|
x1 |
65526 | 838.354ms | 12.794µs ± 16.675µs |
x10 |
65535 | 1.662s | 25.374µs ± 13.587µs |
x100 |
1023 | 1.534s | 1.5ms ± 117.256µs |
x1000 |
7 | 1.014s | 144.971ms ± 3.783ms |
String.appendSlice
Operation | Runs | Total Time | Time/Run (avg ± σ) | Performance Gain vs Competitor |
---|---|---|---|---|
x1 |
65535 | 654.927ms | 9.993µs ± 15.66µs | ~ |
x10 |
65535 | 686.376ms | 10.473µs ± 15.866µs | ⚡ 2.3x Faster ⚡ |
x100 |
32767 | 1.258s | 38.414µs ± 26.291µs | ⚡ 37x Faster ⚡ |
x1000 |
4095 | 1.175s | 286.964µs ± 41.270µs | ⚡ 488x Faster ⚡ |
Calculated using:
(Competitor Time/Run) ÷ (String Time/Run)
It is normal for the values to differ each time the benchmark is run, but in general these percentages will remain close.
The benchmarks were run on a Ubuntu 24.04.1 LTS with 11th Gen Intel® Core™ i5-1155G7 × 8 processor and 32GB of RAM.
The version of zig used is 0.14.0-dev.2265+8a00bd4ce.
The tool used for benchmarking is zBench version 0.9.1.
You can find the source code for this benchmark here: SuperZIG-bench.
Zig provides
std.unicode
, which includes utilities likeUtf8View
andUtf8Iterator
.These are useful and were leveraged in our implementation of Unicode support.
However, they have significant limitations.
Consider the following text:
const txt = "Aأ你🌟☹️👨🏭";
Each character in this string is represented as follows:
Character | Bytes | Codepoints |
---|---|---|
A |
1 | 1 |
أ |
2 | 1 |
你 |
3 | 1 |
🌟 |
4 | 1 |
☹️ |
6 | 2 |
👨🏭 |
11 | 3 |
The only way to iterate over characters using Zig’s standard library is:
const std_view = try std.unicode.Utf8View.init("Aأ你🌟☹️👨🏭");
var std_iter = std_view.iterator();
while (std_iter.nextCodepointSlice()) |res|
std.debug.print("{s}\n", .{res});
Output:
A
╪ú (أ)
Σ╜á (你)
≡ƒîƒ (🌟)
Γÿ╣ (Emoji part of ☹️)
️ (Modifier)
≡ƒæ¿ (👨)
ΓÇì (ZWJ)
≡ƒÅ¡ (🏭)
As you can see, the standard library does not provide a way to iterate over real visual characters (grapheme clusters).
We provide a better approach that allows iterating over grapheme clusters:
var io_iter = try io.unicode.Iterator.init("Aأ你🌟☹️👨🏭");
while (io_iter.nextGraphemeClusterSlice()) |res|
std.debug.print("{s}\n", .{res});
Output:
A
╪ú (أ)
Σ╜á (你)
≡ƒîƒ (🌟)
Γÿ╣∩╕Å (☹️)
≡ƒæ¿ΓÇì≡ƒÅ¡ (👨🏭)
Our library correctly groups Unicode characters into proper grapheme clusters, making it a superior solution for text handling.
#### @JakubSzark/zig-string
#### zg
### Why Our Library is Superior Our library provides:
std.ArrayList
and more.### Final Thoughts We have invested significant time in understanding Unicode and designing an efficient approach. The claim that we have merely “simplified” text handling is inaccurate. Instead, we have innovated and optimized the process to create a truly robust solution.