如何迭代存储在结构中的字符串向量而不移动它们?

问题描述 投票:0回答:1

我有一个大型结构体,其中包含

String
向量作为成员。我正在尝试迭代这些并将每个传递给另一个采用
&str
参数的方法。后一种方法需要
&mut self
才能处理输入流,而我似乎无法协调借用。

我将其归结为一个简单的(?)示例来显示我遇到的问题。请参阅下文,以及指向 Rust 游乐场上的代码

的链接

pub struct Configuration {
    pub prefixes: Vec<String>,
}

impl Configuration {
    pub fn check_one(&mut self, prefix: &str) -> bool {
        "testy".starts_with(prefix)
    }
    pub fn check(&mut self) -> bool {
        for prefix in &self.prefixes {
            if self.check_one(prefix) {
                return true;
            }
        }
        return false;
    }
}

fn main() {
    let mut config = Configuration{ prefixes: vec!["fl".to_string()], };
    dbg!(config.check());
}

实际问题。如果我将参数作为引用 (

self.check_one(&prefix)
),那么我会遇到一个问题,即
self.prefixes
into_iter()
移动,并且编译器建议使用
&self.prefixes
。美好的。但如果我尝试that,我会被告知,当我已经可变地借用它时,我不能一成不变地借用
self
,而且我似乎无法调和这两者。

底线是我需要将成员向量中的每个

String
传递给方法进行检查。我不想修改字符串或向量,但我必须借用
&self
作为可变的。有建议吗?

rust borrow-checker ownership
1个回答
1
投票

当借用

&mut self
的一部分时,无法调用采用
self
的方法。这是因为采用
&mut self
的方法可以修改
self
的任何部分。您无法创建仅允许修改 self 的一部分的
方法

如所写,

check_one
应采用
&self
。在
check
内部,调用
&mut self
时,
&self
会自动转换为
check_one

pub fn check_one(&self, prefix: &str) -> bool {
    "testy".starts_with(prefix)
}

如果您需要能够修改

self
的所有部分,包括
prefixes
,那么您需要克隆
prefixes
,以便
self
拥有的副本不会被借用。

for prefix in &self.prefixes.clone() {
    if self.check_one(prefix) {
        return true;
    }
}

如果您不需要对

prefixes
进行可变或不可变访问,那么您可以执行以下两件事之一。首先,您可以创建一个常规函数来获取
self
的其他部分。

// Replace `()` with the other parts of `self`
pub fn check_one(_other_parts: (), prefix: &str) -> bool {
    "testy".starts_with(prefix)
}
for prefix in &self.prefixes {
    if check_one((), prefix) {
        return true;
    }
}

或者您可以暂时从 prefixes

self
,完成后放回去。

pub fn check(&mut self) -> bool {
    let prefixes = std::mem::take(&mut self.prefixes);
    for prefix in &prefixes {
        if self.check_one(prefix) {
            self.prefixes = prefixes;
            return true;
        }
    }
    self.prefixes = prefixes;
    return false;
}

最后,一种不太常见的情况是

check_one
可能会也可能不会修改
prefixes
,具体取决于数据。在这种情况下,为了仅在必要时克隆
prefixes
,您可以使用
Rc
Arc
。这是一个更广泛的变化。

pub struct Configuration {
    pub prefixes: Rc<Vec<String>>,
}

impl Configuration {
    pub fn check_one(&mut self, prefix: &str) -> bool {
        let check = "testy".starts_with(prefix);
        // Do some mutable work
        if check {
            Rc::make_mut(&mut self.prefixes).push("new".to_string());
        }
        return check;
    }

    pub fn check(&mut self) -> bool {
        // The only change needed here is a dereference
        for prefix in &*self.prefixes.clone() {
            if self.check_one(prefix) {
                return true;
            }
        }
        return false;
    }
}

当您克隆

Rc
本身时,它会创建第二个指向相同数据的只读引用。当存在多个引用时,调用
make_mut
将克隆数据,从而为您提供对数据的可变访问。在这种情况下,第一次调用
make_mut
时,数据将被克隆,
Rc
将用包含克隆数据的新
Rc
替换自身,并返回一个可变引用。如果在同一个循环中再次调用
make_mut
,它只会返回一个可变引用,因为它不再链接到另一个
Rc

© www.soinside.com 2019 - 2024. All rights reserved.