goto is necessary, in some form. function calls a re goto, any branch is goto.
goto is bad when you can do it arbitrarily, especially when the language doesnt provide a less arbitrary goto for whatever purpose you need.
zig has plenty of goto even a somewhat arbitrary one, labelled switch, but its still limited in its own scope and is clearly labelled.
zig doesnt provide a goto as arbitrary as C’s goto or longjump. If you truly need that level of goto your only option in zig is assembly.
I was talking from the perspective of a programmer, developers of languages implement coroutines because programmers want them, because they have used them and like them, because they are relatively easy to learn to use.
calling a subroutine requires (at least) storing an IP (“instruction pointer”) / PC (“program counter”) (on stack) to know where to return to.
jmp / br (and plenty of conditional variants) instructions do not.
Any “systems programming” language can’t do without
keywords, more or less directly compiled into jmp/br
(continue, break, explicit goto and alike)…
… well… even bash has them (continue and break),
but it’s definitely not “systems programming” language, rite?
yes, in C you can jump into a for loop from a code before that loop,
as well as from a code after ttah loop,
but show me who is doing such nuts ever?
Zig has just replaced well-known C’s cleanup pattern (goto __cleanup) with more elaborated defer/errdefer pattern (yes, it’s really a sort of innovation)
setjump/longjump were/are just forerunners for numerous implementations of async/await in various languages
once again
jumps / gotos within a function/subroutine (when used “properly”) are just fine
non-local jumps (yields) are much more harder to deal with in more or less complex code than… than event-driven-state-machines (imho)