How to use struct self in member method closure

Split your data and methods into smaller components, then you can take disjoint borrows to various components on self:

fn fetch_access_token(_base_url: &str) -> String { String::new() }
fn get_env_url() -> String { String::new() }

#[derive(Default)]
struct BaseUrl(Option<String>);

impl BaseUrl {
    fn get(&mut self) -> &str {
        self.0.get_or_insert_with(|| get_env_url())
    }
}

#[derive(Default)]
struct App {
    base_url: BaseUrl,
    access_token: Option<String>,
}

impl App {
    fn new() -> App {
        App::default()
    }

    fn get_access_token(&mut self) -> &str {
        let base_url = &mut self.base_url;
        self.access_token
            .get_or_insert_with(|| fetch_access_token(base_url.get()))
    }
}

fn main() {}

You can go further and do this for both values:

fn fetch_access_token(_base_url: &str) -> String { String::new() }
fn get_env_url() -> String { String::new() }

#[derive(Default)]
struct BaseUrl(Option<String>);

impl BaseUrl {
    fn get(&mut self) -> &str {
        self.0.get_or_insert_with(|| get_env_url())
    }
}

#[derive(Default)]
struct AccessToken(Option<String>);

impl AccessToken {
    fn get(&mut self, base_url: &str) -> &str {
        self.0.get_or_insert_with(|| fetch_access_token(base_url))
    }
}

#[derive(Default)]
struct App {
    base_url: BaseUrl,
    access_token: AccessToken,
}

impl App {
    fn new() -> App {
        App::default()
    }

    fn get_access_token(&mut self) -> &str {
        let base_url = self.base_url.get();
        self.access_token.get(base_url)
    }
}

fn main() {}

Which lets you see that you can abstract out common functionality:

fn fetch_access_token(_base_url: &str) -> String { String::new() }
fn get_env_url() -> String { String::new() }

#[derive(Default)]
struct StringCache(Option<String>);

impl StringCache {
    fn get<F>(&mut self, f: F) -> &str
    where
        F: FnOnce() -> String,
    {
        self.0.get_or_insert_with(f)
    }
}

#[derive(Default)]
struct App {
    base_url: StringCache,
    access_token: StringCache,
}

impl App {
    fn new() -> App {
        App::default()
    }

    fn get_access_token(&mut self) -> &str {
        let base_url = self.base_url.get(get_env_url);
        self.access_token.get(|| fetch_access_token(base_url))
    }
}

fn main() {}

And then you realize the abstraction can be made generic:

fn fetch_access_token(_base_url: &str) -> String { String::new() }
fn get_env_url() -> String { String::new() }

#[derive(Default)]
struct Cache<T>(Option<T>);

impl<T> Cache<T> {
    fn get<F>(&mut self, f: F) -> &T
    where
        F: FnOnce() -> T,
    {
        self.0.get_or_insert_with(f)
    }
}

#[derive(Default)]
struct App {
    base_url: Cache<String>,
    access_token: Cache<String>,
}

impl App {
    fn new() -> App {
        App::default()
    }

    fn get_access_token(&mut self) -> &str {
        let base_url = self.base_url.get(get_env_url);
        self.access_token.get(|| fetch_access_token(base_url))
    }
}

fn main() {}

See also:

Leave a Comment