How to return the owned value of an Option<String>?

Rust is driving me crazy. Since a couple of hours I try to return the string value of an option field in a struct.

This is the code:

lazy_static! {
    pub static ref DATA: Data::new();
}

pub struct Data {
    pub filec: Option<String>,
}

impl Data {
    pub fn new() -> Self {
        Data {
            filec: Option::None,
        }
    }

    pub fn get_filec_content<'a>(&'a mut self) -> &'a str {
        if self.filec.is_none() {
            self.filec = Some(read_file("file.txt"));
        }

        &self.filec.unwrap()
    }
}

pub fn get_filec() {
    // How can I return the content of the file in here?

}

fn read_file(filename: &str) -> String {
    let content = fs::read_to_string(filename)
        .expect(&format!("Failed reading file: {}", filename));
    content
}

In another module, I basically just want to call get_filec() and this should return either a &str with the file content. The functions get_filec_content() is just public, because they need to be public to be called via the lazy_static! macro, or am I wrong?

I clearly loose my mind. It can't be too hard to simply return a string value in rust. But it's driving me crazy, I have tried so many things, and nothing is working.

Thank you for helping me with this (probably super simple) problem.
Regards
max

Unwrapping an Option consumes the Option (you can tell by looking at the signature of the method - it takes self, not &self or &mut self). Therefore, if you do self.filec.unwrap(), you've effectively removed the value of self.filec and left it unassigned, which is not allowed.

What you should do instead, is use the .as_ref() method before calling .unwrap() - this takes an Option<T>, and turns it into a new Option<&T>. Then when you unwrap it, you're only consuming the reference, not the original value.

However, that only gives you Option<&String> - you still then need to transform that into Option<&str>. In a lot of places Rust will do this coercion for you, but this isn't one of them, unfortunately.

pub fn get_filec_content<'a>(&'a mut self) -> &'a str {
     if self.filec.is_none() {
        self.filec = Some(read_file("file.txt"));
     }

    self.filec.as_ref().map(String::as_str).unwrap()
}
4 Likes

You can't unwrap the option because that means the String is moved out. There is Option::as_ref which will take a reference to the value in the option. You can unwrap that:

    pub fn get_filec_content(&mut self) -> &str {
        if self.filec.is_none() {
            self.filec = Some(read_file("file.txt"));
        }

        self.filec.as_ref().unwrap()
    }

Also, next time provide a working playground link.

It's sometimes that simple. But good to know, that unwrapping an option removes the value. This was new for me. Thanks for your good explanation!

1 Like

Thanks for the answer. Also good hint with the playground link. Never thought abouth the playground link before, but it will probably be helpful.

Thank you!

@whois-max The lifetime is inferred by the compiler so it can be left out by the way.

@17cupsofcoffee The compiler does coerce the &String for me: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9fb8849c14499a706b940c0f7068fa89

1 Like

Ah, the case where it doesn't coerce is when you're trying to return an Option<&str> from the function (like this) - my mistake!