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.