如何在成员方法闭包中使用struct self
How to use struct self in member method closure
如何在闭包中调用方法? 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() {}
错误:
生锈 2015
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
生锈 2018
error[E0501]: cannot borrow `self.access_token` as mutable because previous closure requires unique access
--> src/main.rs:25:9
|
25 | / self.access_token
26 | | .get_or_insert_with(|| fetch_access_token(self.get_base_url()))
| |______________------------------_--____________________----________________^ second borrow occurs here
| | | |
| | | first borrow occurs due to use of `self` in closure
| | closure construction occurs here
| first borrow later used by call
error[E0500]: closure requires unique access to `self` but it is already borrowed
--> src/main.rs:26:33
|
24 | pub fn get_access_token(&mut self) -> &String {
| - let's call the lifetime of this reference `'1`
25 | self.access_token
| -----------------
| |
| _________borrow occurs here
| |
26 | | .get_or_insert_with(|| fetch_access_token(self.get_base_url()))
| |_________________________________^^____________________----________________- returning this value requires that `self.access_token` is borrowed for `'1`
| | |
| | second borrow occurs due to use of `self` in closure
| closure construction occurs here
传递给 get_or_insert_with
method in Option<T>
is of type FnOnce
的闭包 - 因此它 消耗 或 移动 捕获的变量。在这种情况下,由于在闭包中使用了 self.get_base_url()
,因此捕获了 self
。但是,由于 self
已经被借用,闭包不能消耗或移动 self
的值以进行唯一访问。
这可以通过使用 get_or_insert
方法来规避,但是无论 access_token
是否为None
。
我会改用这样的东西:
fn fetch_access_token(base_url: &str) -> Result<String, ()> {
let _url = format!("{}/v3/auth/token", base_url);
// ...
let token = String::from("test token");
Ok(token)
}
fn get_env_url() -> String {
String::from("http://www.test.com")
}
pub struct App {
// private fields!
base_url: String,
access_token: Option<String>,
}
impl App {
pub fn new() -> App {
App {
base_url: get_env_url(),
access_token: None,
}
}
/// set new base url; clears cached access token
pub fn set_base_url(&mut self, base_url: String) {
self.base_url = base_url;
self.access_token = None;
}
pub fn get_base_url(&self) -> &str {
&self.base_url
}
/// retrieve (possibly cached) access token. tries again if previous attempt failed.
pub fn retrieve_access_token(&mut self) -> Result<&str, ()> {
if self.access_token.is_none() {
self.access_token = Some(fetch_access_token(&self.base_url)?);
}
Ok(self.access_token.as_ref().unwrap())
}
}
fn main() {
let mut app = App::new();
println!("{}", app.retrieve_access_token().unwrap());
}
将您的数据和方法拆分为更小的组件,然后您可以对 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() {}
您可以更进一步,对两个值执行此操作:
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() {}
这让您看到您可以抽象出通用功能:
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() {}
然后你意识到抽象可以被通用化:
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() {}
另请参阅:
- Borrowing references to attributes in a struct
- The Rust Programming Language chapter on closures,它创建这个缓存结构作为练习的一部分。
如何在闭包中调用方法? 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() {}
错误:
生锈 2015
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
生锈 2018
error[E0501]: cannot borrow `self.access_token` as mutable because previous closure requires unique access
--> src/main.rs:25:9
|
25 | / self.access_token
26 | | .get_or_insert_with(|| fetch_access_token(self.get_base_url()))
| |______________------------------_--____________________----________________^ second borrow occurs here
| | | |
| | | first borrow occurs due to use of `self` in closure
| | closure construction occurs here
| first borrow later used by call
error[E0500]: closure requires unique access to `self` but it is already borrowed
--> src/main.rs:26:33
|
24 | pub fn get_access_token(&mut self) -> &String {
| - let's call the lifetime of this reference `'1`
25 | self.access_token
| -----------------
| |
| _________borrow occurs here
| |
26 | | .get_or_insert_with(|| fetch_access_token(self.get_base_url()))
| |_________________________________^^____________________----________________- returning this value requires that `self.access_token` is borrowed for `'1`
| | |
| | second borrow occurs due to use of `self` in closure
| closure construction occurs here
传递给 get_or_insert_with
method in Option<T>
is of type FnOnce
的闭包 - 因此它 消耗 或 移动 捕获的变量。在这种情况下,由于在闭包中使用了 self.get_base_url()
,因此捕获了 self
。但是,由于 self
已经被借用,闭包不能消耗或移动 self
的值以进行唯一访问。
这可以通过使用 get_or_insert
方法来规避,但是无论 access_token
是否为None
。
我会改用这样的东西:
fn fetch_access_token(base_url: &str) -> Result<String, ()> {
let _url = format!("{}/v3/auth/token", base_url);
// ...
let token = String::from("test token");
Ok(token)
}
fn get_env_url() -> String {
String::from("http://www.test.com")
}
pub struct App {
// private fields!
base_url: String,
access_token: Option<String>,
}
impl App {
pub fn new() -> App {
App {
base_url: get_env_url(),
access_token: None,
}
}
/// set new base url; clears cached access token
pub fn set_base_url(&mut self, base_url: String) {
self.base_url = base_url;
self.access_token = None;
}
pub fn get_base_url(&self) -> &str {
&self.base_url
}
/// retrieve (possibly cached) access token. tries again if previous attempt failed.
pub fn retrieve_access_token(&mut self) -> Result<&str, ()> {
if self.access_token.is_none() {
self.access_token = Some(fetch_access_token(&self.base_url)?);
}
Ok(self.access_token.as_ref().unwrap())
}
}
fn main() {
let mut app = App::new();
println!("{}", app.retrieve_access_token().unwrap());
}
将您的数据和方法拆分为更小的组件,然后您可以对 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() {}
您可以更进一步,对两个值执行此操作:
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() {}
这让您看到您可以抽象出通用功能:
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() {}
然后你意识到抽象可以被通用化:
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() {}
另请参阅:
- Borrowing references to attributes in a struct
- The Rust Programming Language chapter on closures,它创建这个缓存结构作为练习的一部分。