如何在成员方法闭包中使用struct self

Aqr*_*run 8 closures mutability rust borrow-checker

如何在闭包中调用方法?get_access_token方法可以基于以下内容设置新的访问令牌self.get_base_url()

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

fn get_env_url() -> String {
    String::new()
}

pub struct App {
    pub base_url: Option<String>,
    pub access_token: Option<String>,
}

impl App {
    pub fn new() -> App {
        App {
            base_url: None,
            access_token: None,
        }
    }
    pub fn get_base_url(&mut self) -> &String {
        self.base_url.get_or_insert_with(|| get_env_url())
    }
    pub fn get_access_token(&mut self) -> &String {
        self.access_token
            .get_or_insert_with(|| fetch_access_token(self.get_base_url()))
    }
}

fn main() {}
Run Code Online (Sandbox Code Playgroud)

错误:

锈2015

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

fn get_env_url() -> String {
    String::new()
}

pub struct App {
    pub base_url: Option<String>,
    pub access_token: Option<String>,
}

impl App {
    pub fn new() -> App {
        App {
            base_url: None,
            access_token: None,
        }
    }
    pub fn get_base_url(&mut self) -> &String {
        self.base_url.get_or_insert_with(|| get_env_url())
    }
    pub fn get_access_token(&mut self) -> &String {
        self.access_token
            .get_or_insert_with(|| fetch_access_token(self.get_base_url()))
    }
}

fn main() {}
Run Code Online (Sandbox Code Playgroud)

铁锈 2018

error[E0500]: closure requires unique access to `self` but `self.access_token` is already borrowed
  --> src/main.rs:26:33
   |
25 |         self.access_token
   |         ----------------- borrow occurs here
26 |             .get_or_insert_with(|| fetch_access_token(self.get_base_url()))
   |                                 ^^                    ---- borrow occurs due to use of `self` in closure
   |                                 |
   |                                 closure construction occurs here
27 |     }
   |     - borrow ends here
Run Code Online (Sandbox Code Playgroud)

She*_*ter 8

将您的数据和方法拆分为更小的组件,然后您可以对 上的各种组件进行不相交的借用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() {}
Run Code Online (Sandbox Code Playgroud)

您可以进一步为这两个值执行此操作:

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() {}
Run Code Online (Sandbox Code Playgroud)

这让您看到您可以抽象出通用功能:

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() {}
Run Code Online (Sandbox Code Playgroud)

然后你意识到抽象可以变得通用:

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() {}
Run Code Online (Sandbox Code Playgroud)

也可以看看: