返回可选结构体成员的可变引用

3

我希望能够实现对Redis数据库的懒连接。我有一个名为Db的结构体,其中包含Redis Client。默认情况下是None。以下是Python示例代码:

import redis


class Db:

    def __init__(self):
        self.client = None

    def get_client(self):
        if self.client is None:
            self.client = redis.StrictRedis(host='127.0.0.1')
        return self.client

我尝试了这个

extern crate redis;

use redis::Client;

struct Db {
    client: Option<Client>,
}

impl Db {
    fn new() -> Db {
        Db { client: None }
    }

    fn get_client(&mut self) -> Result<&Client, &'static str> {
        if let Some(ref client) = self.client {
            Ok(client)
        } else {
            let connection_string = "redis://127.0.0.1";
            match Client::open(connection_string) {
                Ok(client) => {
                    self.client = Some(client);
                    Ok(&self.client.unwrap())
                }
                Err(err) => Err("Error!"),
            }
        }
    }
}

fn main() {
    let mut db = Db::new();
    db.get_client();
}

我遇到了编译错误。我几乎理解编译器的报错信息,但是不知道如何解决问题。

    error: borrowed value does not live long enough
  --> src/main.rs:28:29
   |
28 |                         Ok(&self.client.unwrap())
   |                             ^^^^^^^^^^^^^^^^^^^^ does not live long enough
29 |                     },
   |                     - temporary value only lives until here
   |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 19:66...
  --> src/main.rs:19:67
   |
19 |         fn get_client(&mut self) -> Result<&Client, &'static str> {
   |                                                                   ^

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:28:29
   |
28 |                         Ok(&self.client.unwrap())
   |                             ^^^^ cannot move out of borrowed content
2个回答

3
如果您调用 unwrap() 方法,那么您会将 TOption 中移出。由于您只是借用了 self,这会导致 cannot move out of borrowed content 错误。
如果您想要借用 Option<T> 中的值,可以使用 as_ref 方法:
extern crate redis;

use redis::Client;

struct Db {
    client: Option<Client>,
}

impl Db {
    fn new() -> Db {
        Db { client: None }
    }

    fn get_client(&mut self) -> Result<&Client, &'static str> {
        if let Some(ref client) = self.client {
            Ok(client)
        } else {
            let connection_string = "redis://127.0.0.1";
            match Client::open(connection_string) {
                Ok(client) => {
                    self.client = Some(client);
                    Ok(self.client.as_ref().unwrap())
                }
                Err(_) => Err("Error!"),
            }
        }
    }
}

fn main() {
    let mut db = Db::new();
    db.get_client().expect("get_client failed");
}

3
OP希望得到一个可变引用;as_ref提供了一个不可变引用。我建议您回答OP所提出的“明确”的问题,以便未来的搜索者可以获得答案。如果这不能回答OP实际想问的问题,那么我会解释为什么在这种情况下它不需要是可变的。 - Shepmaster
可以的,你能解释一下吗? - sinitsynsv

2
所以,我想到了一个解决方案。
struct Db {
    connection: Option<Connection>,
    host: String,
    port: u16,
    db: u8,
}


impl Db {

    fn new(host: String, port: u16, db: u8) -> Db {
        Db {
            host: host,
            port: port,
            db: db,
            connection: None,
        }
    }

    fn get_connection(&mut self) -> RedisResult<&Connection> {
        if let Some(ref connection) = self.connection {
            Ok(connection)
        }
        else {
            let connection_string = format!("redis://{0}:{1}/{2}", self.host, self.port, self.db);
            self.connection = Some(
                Client::open(connection_string.as_ref())?.get_connection()?);
            Ok(self.connection.as_ref().unwrap())
        }
    }

    fn keys(&mut self) -> RedisResult<Vec<String>> {
        let key_iter: redis::Iter<String> = self.get_connection()?.scan()?;
        Ok(key_iter.collect())
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接