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

tet*_*one 4 ownership rust borrow-checker

我有一个大型结构体,其中包含 s 向量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());
}
Run Code Online (Sandbox Code Playgroud)

实际问题。如果我将参数设为引用 ( ),那么我会得到一个由编译器建议移动的self.check_one(&prefix)问题。美好的。但是,如果我尝试这样做,我会被告知,当我已经可变地借用它时,我不能一成不变地借用它,而且我似乎无法调和这两者。self.prefixesinto_iter()&self.prefixesself

最重要的是,我需要将String成员向量中的每个传递给方法进行检查。我不想修改字符串或向量,但我必须借用&self可变的。建议?

dre*_*ato 5

无法调用借用&mut self部分时所采用的方法self。这是因为一个方法&mut self允许修改 的任何部分self。您不能创建一个只允许修改self.

如所写,check_one应采取&self. 里面check&mut self会自动转换为&selfon call check_one

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

如果您需要能够修改 的所有部分self(包括 )prefixes,那么您需要克隆,prefixes以便副本self拥有的内容不被借用。

for prefix in &self.prefixes.clone() {
    if self.check_one(prefix) {
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您不需要对 进行可变或不可变访问prefixes,那么您可以执行以下两件事之一。首先,您可以创建一个常规函数来获取self.

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

或者您可以暂时从 中取出 ,完成后将其放回原处。prefixesself

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

最后,一种不太常见的情况是check_one可能会或可能不会prefixes根据数据进行修改。在这种情况下,为了仅prefixes在必要时进行克隆,您可以使用RcArc。这是一个更广泛的变化。

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

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