We generally don't want the definition of a type to change the requirements of the methods that act on it. I think it would be better to support nonisolated init for something like this. That said, the entire point of an actor is to contain mutable state. If it really only contains immutable state (like your example) then there is no reason for it to be an actor.
The compiler already has an internal notion of "fully initialized" for a type within its init, so it is possible to know this. One wrinkle here is that these is only known after SIL definitive initialization pass, and the isolated/await checks all happen at the AST level, so this may not be trivial to support.
Beyond implementability, there are tradeoffs in predictability/explainability of the model we have to be careful of.
The suggestion to make init() isolated to the context that creates the actor is pure genius
Also, I think the typical use of "home made" actors today (a class with a private serial queue (/ lock)) would probably work the same way: The init() would be invoked by the creator of the class, without really running on the private serial queue, and all other (most other) methods would run on the private serial queue.
So a big +1 from me on "running in creator context" init().
Right, this is the biggest challenge right now: enforcing a different isolation checking based on a flow-sensitive point, within definite initialization. I'm hopeful that I'll be able to come up with a good way to implement this.
This is a good point. Given the lack of precedent for hooks, and possible confusion they could create, I'm less keen on it now than I was earlier.
As of now, I'm still weighing the options while prototyping a very straightforward approach first: limit uses of self in non-async inits, and insert a hop-to-self at the point of definite initialization in async inits. This simple prototype can be molded into something else that relies on flow-sensitivity later on, but for now, it does eliminate the races by extending definite initialization.