In Zig it's going to be a language feature, thanks to its single unit compilation model.
> In the less common case when a program instantiates more than one Io implementation, virtual calls done through the Io interface will not be de-virtualized, as that would imply doubling the amount of machine code generated, creating massive code bloat.
From the article
In any case, if you have two different implementations of something then you have to switch between them somewhere -- either at compile-time or link-time or load-time or run-time (or jit-time). The trick is to find an acceptable compromise of performance, (machine)code-bloat and API-simplicity.
in practice how often are people using more than one io in a program?
You might have a thread pool doing some very specific thing. You can do your own threadpool which wont use the Io interface. But if one of the tasks in the threadpool wanted to read a file, I guess you'd have to pass in the blocking Io implementation.