#value referencing data owned by the current function

29 messages · Page 1 of 1 (latest)

icy linden
#

I'm facing this error again and again and again and still can't build proper mindset to write code without this error
so I have simple function (axum handler)

async fn ip(State(AppState { db, cache }): State<AppState>, Path(ip): Path<Ipv4Addr>) -> String {
  let future = cache
    .lock()
    .unwrap()
    .entry(ip.to_bits())
    .or_insert_with(move || db.get_country(ip).boxed().shared())
    .clone();
  let res = future.await;
  res
}

and the error is

error[E0515]: cannot return value referencing local data `db`
  --> src/main.rs:86:29
   |
86 |     .or_insert_with(move || db.get_country(ip).boxed().shared())
   |                             --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                             |
   |                             returns a value referencing data owned by the current function
   |                             `db` is borrowed here

#

how to fix this?

#

do I need move in closure?

#

why clone is not helping here?

molten harbor
#

db is a local variable captured by the closure, and you're trying to insert into cache a future that borrows db.

#

the reason that move doesn't help is because move only modifies the closure, not the future

#

you need an async move block

#
.or_insert_with(move || {
    async move {
        db.get_country(ip) 
    }.boxed().shared()
})

now the future you are inserting is an async-block future that owns db, and futures can borrow from themselves just fine

#

this is often necessary when calling async methods because most async methods will return borrowing futures

icy linden
#

this gives error

error[E0308]: mismatched types
  --> src/main.rs:59:29
   |
59 |     .or_insert_with(move || async move { db.get_country(ip) }.boxed().shared())
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `String`, found future
   |
molten harbor
#

oops, my bad, should have been:

.or_insert_with(move || {
    async move {
        db.get_country(ip).await
    }
})
icy linden
#

this still not work

#
error[E0308]: mismatched types
  --> src/main.rs:90:29
   |
90 |     .or_insert_with(move || async move { db.get_country(ip).await })
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Shared<Pin<Box<...>>>`, found `async` block
   |
#

why does it say async block instead of real type?

molten harbor
#

argh I accidentally deleted part of the code.

.or_insert_with(move || {
    async move {
        db.get_country(ip).await
    }.boxed().shared()
})
molten harbor
#

I left out the .boxed()

icy linden
#

yes, this is it

#

thank you for your help

#

I'm re-reading your explanation and still hardly understrand how movin in async block change anything

molten harbor
#

it's because get_country() presumably takes &self or &mut self, so that get_country future captures a borrow

#

so the async move block creates a future which owns instead of borrowing the db

restive zodiac
molten harbor
#

nothing here is returning a borrow of a local variable. the whole thing we are doing is not borrowing a local variable

#

but async blocks can borrow their own local variables while they execute.

#

they still can't return a borrow, but awaiting is not returning

icy linden
#

but what I noticed is that with this solution you actually running a future instead of retrning it and run outside of that block

molten harbor
#

the whole boxed async move future is returned

#

to be run later