#Borrow checker

160 messages · Page 1 of 1 (latest)

lone spindle
#

So I am in a fistfight with the borrow checker again. What's new?

fn foo() -> Box<dyn  KeycodeProvider> {

    let (kb_tx, kb_rx) = mpsc::unbounded();

    let mut kb_state = Box::new(AppKb {
        xkb_state: None,
        xkb_keymap: None,
    });
    let mapped_kb_rx= {
        let kb_state_mut = kb_state.as_mut();
        kb_state_mut.map_receiver(kb_rx)
    };
    
    kb_state
}

Here, AppKb implements the KeycodeProvider trait. The map_receiver function stores the provided reference to self in a closure (it basically just maps the stream, but to do that it needs to access xkb_state and xkb_keymap inside the AppKb struct.)

Now, when I try to return kb_state, the borrow checker complains. I don't quite understand why, since presumably the AppKb struct isn't actually moved. It's only the box that's moved, and the closure inside map_receiver doesn't hold a reference to it.
What is the way to solve this? I know I can use Rc<RefCell<>>, but that kind of just seems like a weird workaround.

wanton cedar
#

-errors

peak cloakBOT
#

Run cargo check in a terminal

Note: If using rust analyzer you can click the "click for full compiler diagnostic" link in your editor.

Please post the full output of the above command, including the error title and any help or notes. An example of how this looks is:

error[E0308]: mismatched types
 --> src/main.rs:3:17
  |
3 | let foo: &i32 = bar;
  |          ----   ^^^ expected `&i32`, found integer
  |          |
  |          expected due to this
  |
help: consider borrowing here
  |
3 | let foo: &i32 = &bar;
  |                 +

When posting the error put it in a code block so it has nice formatting:

```rust
// error from cargo check here
```

Please do not post a screenshot. If the output is too long then use a paste tool like https://paste.rs/web

pulsar light
#

we need more context here, in particular what the signatures of AppKb::as_mut and map_receiver are

lone spindle
#

it's part of Box

#

wait

#

actually that was the problem?

#

I could have sworn I tried directly accessing it too

#

alright, thanks

#

actually nevermind.

#

It's because I didn't assign the returned value of map_receiver to a variable

#

here it is:

fn foo() -> Box<dyn  KeycodeProvider> {

    let (_kb_tx, kb_rx) = mpsc::unbounded();

    let mut kb_state = Box::new(AppKb {
        xkb_state: None,
        xkb_keymap: None,
    });
    let _foo = kb_state.map_receiver(kb_rx);
    
    kb_state
}
#

I actually want to return the value returned by kb_state.map_receiver along with kb_state, but the borrow checker doesn't allow it.

pulsar light
#

and what is the signature of map_receiver?

#

you didn't post the full error yet either

lone spindle
#

the signature is this:

    fn map_receiver(
        &mut self,
        kb_rx: mpsc::UnboundedReceiver<crate::delegates::Event>,
    ) -> futures::stream::LocalBoxStream<Event> {

}

And the error is this:
error[E0505]: cannot move out of kb_state because it is borrowed
--> src/app.rs:161:13
|
145 | let mut kb_state = Box::new(AppKb {
| ------------ binding kb_state declared here
...
150 | let res = kb_state.map_receiver(kb_rx);
| -------- borrow of *kb_state occurs here
...
161 | kb_state,
| ^^^^^^^^ move out of kb_state occurs here
...
166 | }
| - borrow might be used here, when res is dropped and runs the destructor for type std::pin::Pin<Box<dyn Stream<Item = traits::Event>>>

pulsar light
#

and what code does that error correspond to? the res variable isn't in either of the snippets you posted

lone spindle
#

whoops, my bad:

error[E0505]: cannot move out of kb_state because it is borrowed
--> src/app.rs:133:5
|
127 | let mut kb_state = Box::new(AppKb {
| ------------ binding kb_state declared here
...
131 | let _foo = kb_state.map_receiver(kb_rx);
| -------- borrow of *kb_state occurs here
132 |
133 | kb_state
| ^^^^^^^^ move out of kb_state occurs here
134 | }
| - borrow might be used here, when _foo is dropped and runs the destructor for type std::pin::Pin<Box<dyn Stream<Item = traits::Event>>>

pulsar light
#

are there any elided lifetimes that haven't been written out in the map_receiver signature?

lone spindle
# pulsar light are there any elided lifetimes that haven't been written out in the `map_receive...

there are no generic lifetimes in the map_receiver signature or the AppKb struct should I add one?
Also here is a mini-example of pretty much what's going on, giving the same compiler error:

trait KeycodeProvider {}

struct AppKb {
    x: i32,
}

impl KeycodeProvider for AppKb {}

impl AppKb {
    fn map_receiver(&mut self) -> impl FnMut() {
        || {
            self.x = 5;
        }
    }
}

pub fn bar() -> Box<dyn KeycodeProvider> {
    let mut app = Box::new(AppKb { x: 0 });
    let closure = app.map_receiver();

    app // error
}

fn main() {}

pulsar light
#

oh huh. i wonder if it has to do with drop order

#

closure borrows from app but if you add an explicit drop(closure) just before the return it compiles

lone spindle
#

I suppose it does

#

unfortunately since I need to return that value it isn't particularly helpful

pulsar light
#

you want to return a value that borrows the box alongside the box itself?

lone spindle
#

pretty much

#

I tried making it a field of the struct, but from what I remember that didn't help

pulsar light
#

that would be what we call a self-referential type

#

you can't have eg a (x, y) tuple where y borrows from x, or a struct field that borrows from another field in the same struct

wise quiver
lone spindle
#

well I am perfectly open to suggestions

pulsar light
#

either move the ownership of app up a level or two, so you don't need to return the owned app from the same function that returns the result of map_receiver, or just let the caller be responsible for calling map_receiver when needed

wise quiver
#

just to make sure this works ?

fn foo() -> Box<dyn  KeycodeProvider> {

    let (kb_tx, kb_rx) = mpsc::unbounded();

    let mut kb_state = Box::new(AppKb {
        xkb_state: None,
        xkb_keymap: None,
    });
    {
      let mapped_kb_rx= {
          let kb_state_mut = kb_state.as_mut();
          kb_state_mut.map_receiver(kb_rx)
      };
    }
    
    kb_state
}
pulsar light
#

that should work yes

lone spindle
#

a

#

actually yeah

pulsar light
#

(note the block which limits the scope of mapped_kb_rx)

lone spindle
#

I didn't see the scope

wise quiver
#

okie just making sure

lone spindle
#

that's also why I am returning a dyn KeycodeProvider

#

I am trying to put a layer of abstraction between the caller and callee here

#

and having the caller call map_receiver afterwards having to pass in the receiver with the unmapped type would ruin that

pulsar light
#

it feels like you're probably making your life more difficult for yourself by trying to do excessive type erasure

lone spindle
#

well perhaps that may be true

#

but at some point I am probably going to have to change what's behind this KeycodeProvider

#

and that would probably include a pretty detrimental amount of new stuff

#

and I don't wanna have to deal with changing a bunch of crap later

pulsar light
#

could you give some more context for how this channel is going to be used, what you mean by the "mapped type", etc?

#

and also what map_receiver returns

#

and how it's meant to be used by the caller

lone spindle
#

the map_receiver returns a futures::stream::LocalBoxStream<traits::Event>

In the current state of the program, traits::Event is this:

pub enum Event {
    RefreshKeybinds,
    Press(KeyCode),
    Exit,
}

And the LocalBoxStream that's mapped from the argument provided to the function, being
kb_rx: mpsc::UnboundedReceiver<crate::delegates::Event>,

is responsible for converting the delegates::Event to traits::Event

delegates::Event being

pub enum Event {
    Ei(EiEvent),
    XKB(XKBEvent),
    Error(PollError),
}
#

where everything inside the enum parameters are just some structs holding data.

#

again, this is how the program is right now, but it's subject to change. The point is, no matter what delegates::Event becomes, and whether it stays delegates::Event at all or becomes something else entirely, should have nothing to do with traits::Event. And the one using the LocalBoxStream returned by map_receiver shouldn't have to care what the events he's receiving are being backed by.

#

and pretty much the only thing keeping that from happening right now is this god damn closure

pulsar light
#

could you have a trait along the lines of

pub trait KeycodeProvider {
    type Event: Into<traits::Event>;
    fn events(&mut self) -> impl Stream<Item = Self::Event>;
}

and then do

fn foo() -> impl KeycodeProvider {
    AppKb {
        xkb_state: None,
        xkb_keymap: None,
    }
}

that way if the event type changes you'd just have to change the type of Event in the KeycodeProvider impl for AppKb. and since foo just returns an opaque impl KeycodeProvider, the caller doesn't know what exactly AppKb::Event is, only that it can be converted to a traits::Event

#

and then the caller would call events() to get the stream of events

lone spindle
#

I'll try it out tomorrow though, because it's gotten really late for me

lone spindle
#

alright. I straight up decided to do this:

pub trait KeyEventProvider: Stream<Item = Event> {
    fn keycode(&mut self, name: &String) -> Result<u32, ProviderError>;
}

Because like this a reference to self is only held when polling, but now I have another problem. I have to provide
mut self: Pin<&mut Self>

and I am not sure how to do that. I return Box<dyn KeyEventProvider> to the caller, but when I try to pin it I get
(dyn KeyEventProvider + 'static) cannot be unpinned

lone spindle
#

*I mean the pin works

#

liek I can do Pin::new

#

but when I try to call .next() on that pin I get the cannot be unpinned error

distant garden
#

can you show whole code

lone spindle
#

mm alright, I'll try to make a short example

#
use futures::{Stream, StreamExt};
use std::pin::Pin;
use std::task::{Context, Poll};

trait KeycodeProvider: Stream<Item = u32> {}
struct AppKb {
    x: u32,
}

impl Stream for AppKb {
    type Item = u32;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        Poll::Ready(Some(self.x))
    }
}
impl KeycodeProvider for AppKb {}

fn get_provider() -> Box<dyn KeycodeProvider> {
    Box::new(AppKb { x: 0 })
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
    let mut provider = get_provider();
    let mut pin = Pin::new(&mut provider);
    pin.next().await;
}

Here is pretty much my exact scenario, and the error is on pin.next()

error[E0277]: dyn KeycodeProvider cannot be unpinned
--> src/main.rs:26:9
|
26 | pin.next().await;
| ^^^^ the trait Unpin is not implemented for dyn KeycodeProvider
|
= note: consider using the pin! macro
consider using Box::pin if you need to access the pinned value outside of the current scope
note: required by a bound in futures::StreamExt::next
--> /home/iexavl/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.31/src/stream/stream/mod.rs:275:15
|
273 | fn next(&mut self) -> Next<'_, Self>
| ---- required by a bound in this associated function
274 | where
275 | Self: Unpin,
| ^^^^^ required by this bound in StreamExt::next

lone spindle
#

yeah, I noticed that a while ago

distant garden
#

the way Stream works

#

like Future

#

is it needs a guarantee the struct won't move after next is called

#

until it is destroyed

#

that's what Pin means

#

so you need the dyn KeycodeProvider to be pinned

#

but here it is not pinned at all

#

indeed nothing is stoping you from using provider after next is called

#

and move the dyn KeycodeProvider wherever you would like

#

thankfully there is a simple solution

#
#[tokio::main(flavor = "current_thread")]
async fn main() {
    let mut provider = Pin::from(get_provider());
    provider.as_mut().next().await;
}
#

here thanks to Pin::from

#

provider is a Pin<Box<dyn KeycodeProvider>>

#

meaning the dyn KeycodeProvider is pinned for good

#

and won't be able to move ever again

#

so everything is alright

#

to be fair, Pinning is not super simple or straightforward for most people

#

it is definitely one of the more complex aspects of rust

#

but it's really really cool imo

lone spindle
#

welp

#

another hour down the train for stupid stuff

#

drain*

distant garden
#

it's not stupid stuff

#

you're learning, next time it'll be easier

lone spindle
#

oh, actually, there is one more thing

#

I have a function that takes in a KeycodeProvider, but it doesn't use it's stream interface

#

it takes a direct reference to KeycodeProvider, not a Pin

#

how is that going to work?

distant garden
#

is it a mutable reference ?

lone spindle
#

right now it is, but if changing it to be non-mutable gets me out of this mess I will

distant garden
#

ok so

#

&mut T allows you to move the T

#

that is always possible

#

therefore

lone spindle
#

I can't pass &mut T while T is pinned Im assuming

distant garden
#

once you pin something, you can't get a &mut to it anymore

#

you can get a & to it easily though

lone spindle
distant garden
#

yes

lone spindle
#

and also, in that case would it be possible to pin it only while polling via next and unpin it when I am not?

distant garden
#

it is a bit complicated

#

first to get the ref

#

you simply do .as_ref().get_ref()

#

as_ref makes it a Pin<&T>

#

and get_ref() a &T

distant garden
#

the guarantee of Pin is it will stay pinned until it is destroyed

#

this is very important, because it allows the receivers to do messed up stuff to the pinned value

#

which means if you did move it, it would completely break down

#

so it really can't move

#

but you did mention "unpin"ning it

#

and that's where the very large escape hatch comes in

#

UnPin

#

some types don't care at all wether they are pinned or not

#

they can't be "messed up"

#

like u32 for example

#

these types are UnPin

#

in fact, almost all types are Unpin

#

if you have an UnPin type, you can pin it and unpin it however you like

#

sadly dyn KeycodeProvider may not be UnPin

#

if you want to only get Unpin KeyCodeProviders

#

you can do

trait KeycodeProvider: Stream<Item = u32> + Unpin {}

or

struct AppKb {
fn get_provider() -> Box<dyn KeycodeProvider + Unpin> {
    Box::new(AppKb { x: 0 })
}

these are different ofc, and only one is necessary. the first says "all KeycodeProviders are Unpin", the second, "all KeycodeProviders returned by get_providerare Unpin"

lone spindle
#

because my KeycodeProvider has an mpsc::UnboundedReceiver in it's fields

#

and sadly that one's not UnPin

distant garden
#

you sure ?

#

what crate are you using ?

lone spindle
#

uh

#

wait

#

okay

#

that's good, yeah?

#

I was pretty sure it didn't implement unpin

#

apparently, I was wrong

#

so now that it implements unpin, I can pass &mut T ?

distant garden
#

if it implements Unpin, you don't need to have a Pin<Box> and can go back to what you were doing originally

#

this is the error you had

error[E0277]: `dyn KeycodeProvider` cannot be unpinned
   --> src/main.rs:26:9
    |
26  |     pin.next().await;
    |         ^^^^ the trait `Unpin` is not implemented for `dyn KeycodeProvider`
    |

#

if you have Unpin, then there is no error

lone spindle
#

damn

#

alright. I will fix some stuff up and try it out, if everything is good ill close the post

#

thanks a lot

lone spindle
#

alright, the goods news is that with the UnPin it seems to work

#

the bad news is there is another issue with the borrow checker that popped up

#

the problems with this thing are like a hydra

#

solve one two more pop up

#

I'm gonna have to get off for now though, I'll probably write here when I get back