Incorrect reference usage

How can I fix this? Of course this is a contrived example but matches a use-case relatively closely. I get the error "returns a value referencing data owned by the current function". I understand the error but how do I fix it?

struct Inner
{
    inner : String
}
struct Outer
{
    outer : RwLock<Inner>
}
impl Outer
{
    fn new() -> Self
    {
        return Self {
            outer : RwLock::new(Inner{inner : "hello".into()})
        }
    }
    
    fn get_a_ref(&self) -> &str
    {
        let inner = self.outer.read().unwrap();
        return &inner.inner;
    }
}
fn main()
{
    let outer = Outer::new();
    let v = outer.get_a_ref();
    println!("{}", v);
}

Thanks in advance

It's not possible to return a reference alone, because then there's nothing to track how long the lock should be locked, but you can return a struct that contains the lock guard and also implements Deref to return &inner.inner.

2 Likes

Your get_a_ref() needs to return something that hangs onto the read lock so the RwLock can track how it is being used.

What about returning impl Deref?

impl Outer {
  fn get_a_ref(&self) -> impl Deref<Target=str> {
    self.outer.read().unwrap()
  }
}

That way anyone calling get_a_ref() will be able to use the string as normal while also making the borrow checker happy.

2 Likes

This works. But is this what you folks were guiding me toward. Any suggestions would be helpful.

use std::sync::{RwLock, RwLockReadGuard};
use std::ops::Deref;
use std::str;

struct Inner
{
    inner : String
}
struct InnerHandle<'a>
{
    handle : RwLockReadGuard<'a, Inner>
}
impl<'a> Deref for InnerHandle<'a>
{
    type Target = str;
    
    fn deref(&self) -> &Self::Target
    {
        return self.handle.inner.as_str();
    }
}
struct Outer
{
    outer : RwLock<Inner>
}
impl Outer
{
    fn new() -> Self
    {
        return Self {
            outer : RwLock::new(Inner{inner : "hello".into()})
        }
    }
    
    fn get_a_ref(&self) -> InnerHandle
    {
        return InnerHandle{handle : self.outer.read().expect("")};
    }
}
fn main()
{
    let outer = Outer::new();
    let v = outer.get_a_ref();
    let val : String = (*v).into();
    println!("{}", val);
}

Yes, that's exactly what I had in mind.

The main difference is my version says you can only dereference the return value to get a str (meaning I can return the RwLockReadGuard directly), while yours creates a new type wrapping the RwLockReadGuard and names it, letting you add domain specific methods or functionality as needed.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.