StaticStrings.jl v0.3 was just released and has joined the JuliaStrings Github organization. Special thanks to @PatrickHaecker for major contributions to this release.
StaticStrings.jl contains several string types which wrap a NTuple{N, UInt8} for strings of fixed length strings similar to InlineStrings.jl. The main difference is that StaticStrings.jl uses an NTuple whereas InlineStrings.jl uses integer types as a backing store.
StaticStrings on GPU, code_warntype on StaticString generation (#20)
Upstream issue for JuliaLang/Julia
In reviewing Patrick’s work, I realized that Julia is missing facilities to populate a Vector{UInt8} or Memory{UInt8} from a NTuple{N,UInt8} efficiently. I created an issue documenting the lack of autovectorization for this route and potential new methods for copyto!.
I was originally inspired to create this package to address fixed length strings coming from I/O libraries such as HDF5.jl. I never finished actually integrating this with I/O packages, so I hope to pursue that.
I also noticed that an AbstractString that wraps an existing Memory{UInt8} may be useful. I am unsure whether to add this to StaticStrings or if I should start a new package.
Technically definitely interesting. However, from an ecosystem point-of-view it will probably take quite some time until StringView is available in Julia lts. While Julia 1.13 has the potential to be the next lts, an lts with StringView could still be 3 to 4 years away.
So in order to be useful now, this probably would need to be behind an interface with a fallback to a different implementation for older Julia versions with the same functionality only being slower.
I believe the core devs are aiming for 1.14 as the new LTS, but, like previous LTS releases, this will only be decided when 1.15 is released (as then can then retroactively judge if 1.14 was good enough to become LTS). So we’ll probably have StringView in the next LTS.
Indeed, @mkitti and @PatrickHaecker is it one of the design goals of StringView strings could be implemented in terms of them. If you want to use string views on Julia versions < 1.14, you should use StringViews.jl. This is an established package, and is the implementation that has been ported to Base. On Julia version 1.14 and newer, StringViews.jl is a stub package that simply re-exports the Base type and does nothing else. StringViews.jl is compatible with Julia 1.6.
I recommend you move the implementation to using string views, and then upstream PRs to any issues. Other than it being slightly annoying to have to upstream issues to Base, in theory there should be no downside, and adhering to one underlying implementation would help consolidate the semantics around strings-versus-codeunits in Julia.
I tried this package for the first time. FWIW, compile-time parsing works:
julia> using StaticStrings
julia> const s = static"123";
julia> function sum_digits()
sum(convert(Int, c-UInt8('0')) for c in codeunits(s))
end
sum_digits (generic function with 1 method)
julia> sum_digits()
6
julia> @code_typed sum_digits() # the compiled code is a trivial return
CodeInfo(
1 ─ return 6
) => Int64
Realize that the StringView type in Juia 1.14 is essentially just the code from StringViews.jl (which supports Julia 1.6+) ported into Base (with a little bit of code de-duplication). Performance-wise, there should be essentially no difference.
An implementation using StringViews and StaticArrays seems like it could be pretty minimal. If you do:
using StringViews, StaticArrays
const StaticString{N} = StringView{SVector{N, UInt8}} # type alias
macro static_str(s)
bytes = tuple(codeunits(s)...)
return :(StringView(SVector($bytes)))
end
then a lot of things just work:
julia> s = static"🚀"
"🚀"
julia> typeof(s)
StringView{SVector{4, UInt8}}
julia> s isa StaticString{4}
true
julia> s[1]
'🚀': Unicode U+1F680 (category So: Symbol, other), input as \:rocket:<tab>
julia> codeunits(s)
4-element SVector{4, UInt8} with indices SOneTo(4):
0xf0
0x9f
0x9a
0x80
However, if you want to define custom operations, like static concatenation of two static strings, then seems like it will be tricky right now to avoid type piracy unless you define a wrapper type and forward a lot of methods, or use a different function name (rather than overloading a Base function like *), or push the implementation into StaticArrays.jl.
I guess it depends on what kind of use-case you have in mind. If you just want to store them efficiently (e.g. in an array or an HDF5 file), then it’s not clear whether you need much more than the type above.
(If the implementation becomes small enough, it’s not crazy to me to simply make SString a type defined in StaticArrays.jl … then you can write whatever custom methods you want and it won’t be type piracy.)