For Regexes that don’t change for the life of my (Swift 6) app, it seems wasteful to create them just before each use and them throw them away immediately after. So I am looking to hoist them to higher scopes and create them just once, where possible.
This is fine if there’s a known isolation domain (e.g., @MainActor), but sometimes there isn’t one, as in the trivial example case of an extension to String shown below.
(Yes, I could stop using an extension and instead make multiple plain functions, with one copy of the code in each isolation domain where I want to use it, but that duplication is not appealing for large chunks of code.)
If I “force” it to work by declaring the Regex to be nonisolated(unsafe), then it will build, but I’m left wondering if it’s actually safe. Assume all I ever do with this Regex is match against it using methods like (firstMatch(), String's contains(), replacing(), etc.)
Is matching against a Regex a read-only operation that is safe to do from multiple threads simultaneously? Or is that unsafe annotation an accurate description of this practice?
// Without the "(unsafe)" part, Swift 6 complains: 'nonisolated'
// can not be applied to variable with non-'Sendable' type
// 'Regex<(Substring, Substring)>'
nonisolated(unsafe)
let twiddleRegex : Regex<(Substring, Substring)> =
try! Regex("(foo|bar)").ignoresCase()
extension String {
var twiddled : String {
return self.replacing(twiddleRegex) { match in
match.1.uppercased()
}
}
}
print("Foo test bar test".twiddled) // FOO test BAR test