askama icon indicating copy to clipboard operation
askama copied to clipboard

problem accessing self.vec in for loop because "cannot move out of dereference"

Open yatesco opened this issue 2 years ago • 7 comments

Same problem as #107, but I can't see the issue with my code.

The error:

cannot move out of dereference of `std::sync::RwLockReadGuard<'_, Vec<std::string::String>>`
move occurs because value has type `Vec<std::string::String>`, which does not implement the `Copy` trait

The code:

#[derive(Template, Debug)]
#[template(
    source = "
    Time is now {{ now }}<ul>{% for i in patients.read().unwrap() %}<li>{{i}}</li>{% endfor %}</ul>
    ",
    ext = "txt",
    print = "code"
)]

pub(crate) struct PatientListView {
    patients: Arc<RwLock<Vec<String>>>,
}

impl PatientListView {
    fn debug(&self) {
        for i in self.patients.read().unwrap().iter() {
            println!("i: {}", i);
        }
    }
}

Help :-)

yatesco avatar Dec 15 '21 10:12 yatesco

In the generated code askama invokes Vec::into_iter(), which tries to consume the vector, which gives to the error you posted. You need to "convince" askama to use an Iterator instead of an IntoIterator by calling .iter() explicitly. This means that you have to store the read guard in a variable first, though:

#[template(
    source = "
    {%- let guard = patients.read().unwrap() -%}
    <ul>{% for i in guard.iter() %}<li>{{i}}</li>{% endfor %}</ul>
    {%- let _ = guard -%}
    ",
    ext = "txt",
    print = "code"
)]

Kijewski avatar Dec 15 '21 10:12 Kijewski

Hmm, maybe we should have the generator produce a variable for the for-loop expression instead. Then we avoid situations where that is needed to be done manually.

Additionally, the for-loop might be needed to be wrapped in an addiction block. To avoid locks not being released after the for-loop.

Thoughts @Kijewski @djc?

vallentin avatar Dec 15 '21 11:12 vallentin

@vallentin, the iterator is stored in _iter.

The problem is that calling iter() in line 644 fails some test with "type annotations needed", so I guess we are stuck with the into_iter() call.

Additionally, the for-loop might be needed to be wrapped in an addiction block.

You are right! I added {%- let _ = guard -%} to my previous comment.

Kijewski avatar Dec 15 '21 11:12 Kijewski

In the generated code askama invokes Vec::into_iter(), which tries to consume the vector, which gives to the error you posted. You need to "convince" askama to use an Iterator instead of an IntoIterator by calling .iter() explicitly. This means that you have to store the read guard in a variable first, though:

#[template(
    source = "
    {%- let guard = patients.read().unwrap() -%}
    <ul>{% for i in guard.iter() %}<li>{{i}}</li>{% endfor %}</ul>
    {%- let _ = guard -%}
    ",
    ext = "txt",
    print = "code"
)]

This works perfectly. thank you

yatesco avatar Dec 15 '21 11:12 yatesco

the iterator is stored in _iter.

Ahh, right

The problem is that calling iter() in line 644 fails some test with "type annotations needed", so I guess we are stuck with the into_iter() call.

Back to the drawing board then :)

vallentin avatar Dec 15 '21 12:12 vallentin

I don't know if it would be possible to automatically divine when it's possible to call .iter(). I guess if it's possible to use .iter(), then you also want to use .iter() instead of .into_iter() in 99% of the use cases. But actually I did not pay enough attention why a type annotation is needed in here, though. :-/

Kijewski avatar Dec 15 '21 13:12 Kijewski

A for loop always calls into_iter() on its Expression. If the template calls iter() on it, the resulting type should already be an Iterator and so calling into_iter() on that should have no effect.

djc avatar Dec 15 '21 13:12 djc