Using struct marked with lifetime as the argument to `async_trait` function

struct Foo<'a>{
    a:Option<&'a mut i32>
}

#[async_trait]
trait Trait<'a>{

    async fn fun(&self, arg:&'a mut Foo);
}

#[async_trait]
impl<'a> Trait<'a> for Bar  {
    async fn fun(&self, arg:&'a mut Foo) {
        let val:&&mut i32 = arg.a.as_ref().expect(""); //line 14
        **val = 100; //line 15
}

Problem :
I have above code where struct Foo whose member either can take Some(&mut i32) or None'.So i have marked it as Option<&'a mut i32>`.

Also I have a Trait which is marked with #[async_trait](because it has async fn fun() inside it) and it is implemented by another empty struct Bar.one of the argument to async fn fun() is Foo .

Challenges/questions:

  • at line no 14 i want to change the value of val but it is failing with below message.
    (Note:I'm forced to use as_ref()by compiler at line 14 )
14 |         let val = arg.a.as_ref().expect("");
   |             --- help: consider changing this to be a mutable reference: `&mut &mut i32`
15 |         **val = 100;
   |         ^^^^^^^^^^^ `val` is a `&` reference, so the data it refers to cannot be written

  • Instead of having arg:&'a mut Foo to fun() i want to have something like arg:mut Foo .
    beacuse val will be of type &mut i32 (which is simpler to use) not &&mut i32. is this even possible in this case?
  • Is there a better way in rust to write the similar code ?any suggestions?

Note: this code is straightforward if i dont have #[async_trait] around Trait . I wonder what is the issue with async-trait :frowning:

Your questions is rather difficult to read, and for that reason I don't know if this is helpful, but here are some thoughts:

  1. You should generally avoid lifetimes on structs and traits.
  2. The as_ref function always returns an Option<&T> even if called on a mutable reference. Use as_mut to get an Option<&mut T>.

But in my case I need to have & and mut & member as part of Foo . is there a better way to do this ? I guess compiler will through error if we do not specify lifetime

&&mut i32 is functionally the same as &&i32 (modulo possible variance differences, not sure if there are any) - the "shared" part of external reference overrides the "exclusive" part of the internal one. To change some value, you need to have exclusive (i.e. mutable) references in the whole chain. Not sure what stops you from using arg.a.as_mut().expect("") in line 14?

thanks!! I can use as_mut() . But here even after using as_mut(), type of val is &mut &mut (which is like double reference) what I want is something like &mut (single reference) . any suggestion on this?

Well, you can use take().expect("...") instead of as_mut().expect() to temporarily remove the reference from the Option, use it, then put it back. Not sure how this can be connected to async_trait, however, so it's possible that your case is more complicated and double-reference is unavoidable.

To be more precise, &'a &'b mut T is functionally the same as &'a &'a T, and in general there’s also a variance difference w.r.t. whether or not T is covariant in this type; though for T being i32 this variance doesn’t matter/apply.

Note that when x: &'a mut &'b mut T, you can dereference it to &mut **x which is a value of type &'a mut T. Rust even supports just writing *x for this operation ([1]).

So let val: &mut i32 = *arg.a.as_mut().expect(""); works. There’s also the method Option::as_deref_mut which can dereference a pointer type in an &mut Option<…> for you. So let val: &mut i32 = arg.a.as_deref_mut().expect(""); works, too.


  1. the so-called “reborrowing” that the extra “&mut *” does can happen implicitly ↩︎

2 Likes

thanks ! I totally forgot use something like this . such a easy way !!
:slight_smile: