Disclaimer: I am not an actor expert and I don't know anything about actors in Rust; I just read a couple of books on Erlang such as the excellent book "Designing for Scalability with Erlang/OTP" written by Francesco Cesarini.
I personally think that actors are true microservices in the sense that:
Therefore, an actor system is not always the ideal design. For example, I think that Futures or async/await are much better for data processing pipelines that do not require state management. Or that designing an actor system for bank transfers is not trivial because of the nature of bank transactions that makes the system state hard to split.
Still, I find the actor system a very elegant solution in other cases. For example, in his book "Designing for Scalability with Erlang/OTP", Francesco Cesarini demonstrates how to implement a coffee machine in Erlang, and I find the resulting code, in particular the handling of invalid actions, impressive:
I think that actors are a good option if a large distributed system state can be split into smaller parts. For example, let's consider a system to book a ticket with a seat number at a concert. The system has to store for each seat if a seat is free, being reserved or reserved, and, if it is not free, the person who is reserving or has reserved the seat. We could implement this system as follows:
If the concert venue has lots of seats, and the number of person interested in getting a ticket is high, a single BookingManager actor cannot handle all the requests quickly. In this case, we need multiple BookingManager actors, and a way to share the state of the seats between them. One way to design the system consists in splitting the seats state across multiple SeatsAreaManager actors, each of them being responsible of the seats of a distinct area of the concert venue. The booking process would work as follows:
This change in the design enables to scale the number of BookingManager and the number of SeatsAreaManager (the maximum number of SeatsAreaManager is the number of seats in the concert venue).
If the number of SeatsAreaManager actors is high, BookingManager actors can become slow because they need to query all the SeatsAreaManager to know which seats are free. In this case, we could introduce additional levels of SeatsAreaManager actors who would not store state but contact SeatsAreaManager actors at the level below to gather free seats in their areas. Another option could consist in making BookingManager actors store a cache of the free seats that would be frequently refreshed, for example every 100ms.
I hope that this example makes sense and helps to show the power of actors to scale a system. I am not sure how easy it would be to develop such a system using Futures or async/await because I don't see at the moment how such constructs could help to handle this type of "distributed state".
Note on the "Let it crash" paradigm: The point of the "Let it crash" is that the actor that crashes cannot properly propagate the error to other actors it was in contact with. Instead, other actors should be notified of the crash and will take care of updating their state accordingly. For example, if the Client C crashes, this crash could notify the BookingManager actor B that Client C was in contact with. BookingManager B could then decide to cancel any reservation in progress done by Client C.