I'm trying to learn how to update state in an actix-web server.
See my questions in the comments here:
As a first commentary: you do not need to use Arc
here. The type annotation for your line look like this:
let state: Arc<_> = Arc::clone(&state);
But, you could do
let state: web::Data<_> = state.clone();
And the result should be almost identical
In fact you can then always get back a reference to Arc
, since web::Data implements Deref<Target = Arc>.
I am not sure why you want that in this case though: using the state
you give as argument should work just fine.
For your second question: you do not, in fact, mutate state.dog_map
(at least not directly from what I can see), but a reference to it. The mut
in let mut dog_map = &state.dog_map
means that dog_map
can be reassigned to point at something else (since it is a reference), like
let mut dog_map = &state.dog_map;
dog_map = &other_state.dog_map;
Are you suggesting that I can just modify state.dog_map
directly?
This does not work: state.dog_map.insert(id, dog.clone());
The error is "cannot borrow data in an Arc
as mutable trait DerefMut
is required to modify through a dereference, but it is not implemented for Arc<AppState>
"
To be able to mutate data stored in an Arc
(or a Data
, which is just a wrapper for an Arc
), you need some way of making those mutations thread-safe.
The simplest way of doing this would be to wrap AppState
(or individual fields of AppState
, if you want to be more fine grained) in a Mutex
, which allows you to 'lock' the data so that only one thread can access it at a time. There's also RwLock
, which allows multiple readers or one writer, but never both.
However, since you're specifically using a HashMap, you might want to look into concurrent data structures like dashmap
, which are specifically designed to be concurrently accessed.
The example for Data
in the Actix docs shows the Mutex
approach. There's also a chapter on Arc
/Mutex
in the book.
Okay, I'm trying the approach of using a Mutex. It works for everything but the post handler.
See this comment: rust-actix-web-demo/main.rs at master · mvolkmann/rust-actix-web-demo · GitHub
You can't mutate via a &
reference - either change it to &mut
or just call the method directly via lock.dog_map
.
I'm guessing the line with insert
is also emitting an error about the immutable reference.
That fixed my post route. Thanks! But it also broke my GET routes. They still return a 200 status, but no response body. All I did was add use of Mutex
. See rust-actix-web-demo/main.rs at master · mvolkmann/rust-actix-web-demo · GitHub.
I added println!("{:?}", dogs);
in the get_dogs
function to verify that it is getting the data from the HashMap
and it is. So I don't know why it is no longer being returned in the response body.
I finally got time to look at this again. Turns out it was working, but I had clicked the "Visualize" button in Postman and that causes it to not render the JSON that came back. So sorry for the noise and thanks so much for the help!