bython
bython

Reputation: 117

How to return a struct whose property has a type of &str from a function in rust?

I am trying to do the following

// Just removes redundant white space while also splitting the string
// with a whitespace.
fn split_whitespace(string: &str) -> Vec<&str> {
    let words: Vec<&str> = string
        .split_whitespace()
        .filter(|&word| word != "")
        .collect();
    words
}

pub fn get_websites_from_hosts(hosts_path: &Path) -> Result<Vec<Website>, std::io::Error> {
    let hosts_string = read_to_string(hosts_path)?;

    let result: Vec<Website> = hosts_string
        .lines()
        .filter(|&line| split_whitespace(&line.to_owned()).len() == 2)
        .map(|line| {
            let ip_domain = split_whitespace(&line.to_owned());
            Website {
                domain_name: ip_domain[1], // This won't work because it is refering hosts_string which is a local variable of the function
                redirect_ip: ip_domain[0], // the same case with this one.
                is_blocked: true,
                hosts_path,
            }
        })
        .collect();

    Ok(result)
}

What I want to achieve is to successfully return the Website struct while probably not editing the struct declaration.

And here is the Website struct declaration

#[derive(Debug, Clone)]
pub struct Website<'a> {
    pub domain_name: &'a str,
    pub redirect_ip: &'a str,
    pub is_blocked: bool,
    pub hosts_path: &'a Path,
}

And here is the error

error[E0515]: cannot return value referencing temporary value
  --> src/parser.rs:21:13
   |
20 |               let ip_domain = split_whitespace(&line.to_owned());
   |                                                 --------------- temporary value created here
21 | /             Website {
22 | |                 domain_name: ip_domain[1],
23 | |                 redirect_ip: ip_domain[0],
24 | |                 is_blocked: true,
25 | |                 hosts_path,
26 | |             }
   | |_____________^ returns a value referencing data owned by the current function

For more information about this error, try `rustc --explain E0515`.
error: could not compile `website-blocker` due to previous error
warning: build failed, waiting for other jobs to finish...
error: could not compile `website-blocker` due to previous error

I would love a deep explanation and general tips to use when encountering this and the like problems

Upvotes: 0

Views: 632

Answers (1)

PitaJ
PitaJ

Reputation: 15012

The &str references in your struct refer to segments of the string returned by read_to_string. That string (hosts_string) is dropped and deallocated at the end of fn get_websites_from_hosts, so if you could return references to it, they would be dangling. Rust prevents you from doing that.

Instead, you'll need to either move the read_to_string outside that function, and pass the string in as an &str or change your struct to hold owned values like String, IpAddr, etc.

#[derive(Debug, Clone)]
pub struct Website<'a> {
    pub domain_name: String,
    pub redirect_ip: IpAddr,
    pub is_blocked: bool,
    pub hosts_path: &'a Path,
}

If you want to avoid some String allocations, you could use a "small-string optimization" library like compact_str

#[derive(Debug, Clone)]
pub struct Website<'a> {
    // Stores a domain name of up to 24 bytes in place,
    // without an extra heap allocation
    pub domain_name: CompactString,
    pub redirect_ip: IpAddr,
    pub is_blocked: bool,
    pub hosts_path: &'a Path,
}

Upvotes: 2

Related Questions