the key feature is the "composability" of the RAII technique.
I think it might be possible to have composability in RAII in Go if such objects weren't allowed to live anywhere but the stack. (Which would limit passing through channels.) Those objects would have a well defined creation and destruction time. While not completely universal, this might still be quite powerful and useful.
It would. (In fact, you might be able to do this today with runtime.SetFinalizer.)
You do have to be careful to avoid bringing back the objects that your finalizer references from the grave. If you do that in Go and create cycles, it leaks (which isn't unique to Go, many other languages do this). You need a sophisticated GC (like Java's) or a type system that's aware of the heap/stack distinction of objects (like Rust's) to mitigate that.
I think it might be possible to have composability in RAII in Go if such objects weren't allowed to live anywhere but the stack. (Which would limit passing through channels.) Those objects would have a well defined creation and destruction time. While not completely universal, this might still be quite powerful and useful.