【问题标题】:Mutable borrow to function for struct holding reference issues可变借用以解决结构持有引用问题
【发布时间】:2015-08-10 10:55:11
【问题描述】:

我作为可变引用传递的结构存在问题。仅当将结构定义为保存引用时才会出现此问题。

struct Global<'a> {
    one: i32,
    two: &'a i32
}

fn do_task<'a, F, R: 'a>(global: &'a mut Global<'a>, task: F)
    where F: Fn(&'a mut Global<'a>) -> &'a R {
    let result = task(global);
}

fn one<'a>(global: &'a mut Global<'a>) -> &'a i32 {
    &global.one
}

fn two<'a>(global: &'a mut Global<'a>) -> &'a i32 {
    global.two
}

fn main() {
    let number = 2;
    let mut global = Global {
        one: 1,
        two: &number
    };
    do_task(&mut global, one);
    do_task(&mut global, two);
}

借用检查器抱怨以下内容:

error: cannot borrow `global` as mutable more than once at a time
do_task(&mut global, two);
             ^~~~~~
note: previous borrow of `global` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `global` until the borrow ends
do_task(&mut global, one);
             ^~~~~~
note: previous borrow ends here
fn main() {
...
}
^

但是,如果我更改代码以使字段 two 不是引用,如下例所示,那么它通过了。

struct Global {
    one: i32,
    two: i32,
}

fn do_task<'a, F, R: 'a>(global: &'a mut Global, task: F)
    where F: Fn(&'a mut Global) -> &'a R {
    let result = task(global);
}

fn one<'a>(global: &'a mut Global) -> &'a i32 {
    &global.one
}

fn two<'a>(global: &'a mut Global) -> &'a i32 {
    &global.two
}

fn main() {
    let mut global = Global {
        one: 1,
        two: 2
    };
    do_task(&mut global, one);
    do_task(&mut global, two);
}

我尝试用另一个作用域包围 do_task 函数调用,但没有效果。

为什么让引用将可变借用扩展到 main 的末尾,有什么办法解决这个问题?

我正在使用rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14)

【问题讨论】:

    标签: rust


    【解决方案1】:

    问题是您不小心将Global 的通用生命周期参数与可变借用的生命周期绑定在一起。

    当您需要&amp;'a mut Global&lt;'a&gt; 时,这意味着Global 的可变借用的持续时间必须与two 中的引用一样长——因此,Global 存在的完整长度。因此,当您编写&amp;mut global 时,您已推断为永久借用global。我会这样写,语法不是很有效,但明白了重点:

    fn main() {
        'a: {
            let number: i32 + 'a = 2;
            let mut global: Global<'a> = Global {
                one: 1,
                two: &'a number,
            };
            do_task(&mut global: &'a mut Global<'a>, one);
            do_task(&mut global: &'a mut Global<'a>, two);
        }
    }
    

    每个&amp;mut global 都在借用global,直到'a 块结束,因此第二个与第一个发生冲突。

    您希望分别对待这两个生命。该函数应该绑定一个生命周期参数,而不是使用一个具体的生命周期:这是F: for&lt;'b&gt; Fn(&amp;'b mut Global) -&gt; &amp;'b R,这样读:“F 应该,给定任意生命周期'b,实现Fn(&amp;'b mut Global) -&gt; &amp;'b R”。实际的函数会在写入时去掉 Global 的生命周期参数,因此可以推断为另一个任意生命周期,如下所示:

    fn one<'a>(global: &'a mut Global) -> &'a i32 {
        &global.one
    }
    

    Here’s the final result:

    struct Global<'a> {
        one: i32,
        two: &'a i32
    }
    
    fn do_task<F, R>(global: &mut Global, task: F)
    where F: for<'a> Fn(&'a mut Global) -> &'a R {
        let result = task(global);
    }
    
    fn one<'a>(global: &'a mut Global) -> &'a i32 {
        &global.one
    }
    
    fn two<'a>(global: &'a mut Global) -> &'a i32 {
        global.two
    }
    
    fn main() {
        let number = 2;
        let mut global = Global {
            one: 1,
            two: &number
        };
        do_task(&mut global, one);
        do_task(&mut global, two);
    }
    

    【讨论】:

    • 谢谢。您是否有关于 for&lt;&gt; 语法的文档链接?我在书、参考资料或 RFC 中找不到任何内容。
    • @Dale,很可能它还没有在书中记录,但here 是一个引入这种语法的 RFC。它还解释了其背后的基本原理。顺便说一句,它被称为更高级别的特征边界。
    猜你喜欢
    • 1970-01-01
    • 2023-01-08
    • 1970-01-01
    • 1970-01-01
    • 2012-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-12
    相关资源
    最近更新 更多