- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一段代码需要存储 String
并访问对这些字符串的引用。我先是这样写的:
struct Pool {
strings : Vec<String>
}
impl Pool {
pub fn new() -> Self {
Self {
strings: vec![]
}
}
pub fn some_f(&mut self) -> Vec<&str> {
let mut v = vec![];
for i in 1..10 {
let string = format!("{}", i);
let string_ref = self.new_string(string);
v.push(string_ref);
}
v
}
fn new_string(&mut self, string : String) -> &str {
self.strings.push(string);
&self.strings.last().unwrap()[..]
}
}
这没有通过借用检查器:
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/main.rs:19:30
|
14 | pub fn some_f(&mut self) -> Vec<&str> {
| - let's call the lifetime of this reference `'1`
...
19 | let string_ref = self.new_string(string);
| ^^^^ mutable borrow starts here in previous iteration of loop
...
23 | v
| - returning this value requires that `*self` is borrowed for `'1`
显然,借用检查器不够聪明,无法意识到可变借用不会超出对 new_string
的调用。我尝试将改变结构的部分与检索引用分开,得到以下代码:
use std::vec::*;
struct Pool {
strings : Vec<String>
}
impl Pool {
pub fn new() -> Self {
Self {
strings: vec![]
}
}
pub fn some_f(&mut self) -> Vec<&str> {
let mut v = vec![];
for i in 1..10 {
let string = format!("{}", i);
self.new_string(string);
}
for i in 1..10 {
let string = &self.strings[i - 1];
v.push(&string[..]);
}
v
}
fn new_string(&mut self, string : String) {
self.strings.push(string);
}
}
这在语义上是等效的(希望如此)并且可以编译。然而,将两个 for
循环合并为一个:
for i in 1..10 {
let string = format!("{}", i);
self.new_string(string);
let string = &self.strings[i - 1];
v.push(&string[..]);
}
给出类似的借用错误:
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/main.rs:19:13
|
14 | pub fn some_f(&mut self) -> Vec<&str> {
| - let's call the lifetime of this reference `'1`
...
19 | self.new_string(string);
| ^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
20 | let string = &self.strings[i - 1];
| ------------ immutable borrow occurs here
...
24 | v
| - returning this value requires that `self.strings` is borrowed for `'1`
我有几个问题:
在这种情况下,为什么借用检查器如此严格,以至于在整个循环期间扩展可变借用?是否不可能/很难分析传递给 new_string
的 &mut
不会泄漏到该函数调用之外?
是否可以通过自定义生命周期解决此问题,以便我可以返回到既可以变异又可以返回引用的原始助手?
是否有一种不同的、更符合 Rust 习惯的方式,它不会扰乱借用检查器,我可以在其中实现我想要的,即拥有一个可以变异并返回对自身的引用的结构?
我找到了 this question ,但我不明白答案(这是对 #2 的否定回答吗?不知道),而且大多数其他问题都存在显式生命周期参数的问题。我的代码仅使用推断的生命周期。
最佳答案
在这种情况下,借用检查器不允许这样做是正确的:
self.new_string(string);
let string = &self.strings[i - 1];
v.push(&string[..]);
self.new_string
可能会导致您推送到 v
的所有先前引用变得无效,因为它可能需要为 strings
分配内存> 并移动其内容。借用检查器会捕捉到这一点,因为您推送到 v
的引用需要一个生命周期来匹配 v
,所以 &self.strings
(因此 &self
) 必须为整个方法借用,这可以防止你的可变借用。
如果您使用两个循环,则在您调用 new_string
时没有事件的共享借用。
你可以看到问题不是被扩展的可变借用,在这个(完全无用的)版本的循环中,编译:
for i in 1..10 {
let string = format!("{}", i);
self.new_string(string);
let mut v2 = vec![];
let string = &self.strings[i - 1];
v2.push(&string[..]);
}
至于更惯用的方式,Vec 类可以自由地使可变操作中的引用无效,因此您不能在 safe rust 中做您想做的事。即使编译器允许,您也不想使用 C++ 向量来执行此操作,除非您预先分配向量并手动确保永远不会推送比最初分配的元素更多的元素。显然 Rust 不希望您手动验证程序的内存安全性;预分配的大小在类型系统中不可见,借用检查器也无法检查,因此这种方法是不可能的。
即使使用像 [String; 这样的固定大小的容器,你也无法解决这个问题。 10]
。在那种情况下可能没有分配,但实际上使这种安全的是你永远不会更新你已经从中引用的索引的事实。但是 Rust 没有从容器中部分借用的概念,所以没有办法告诉它“有一个共享借用到索引 n,所以我可以从索引 n + 1 进行可变借用”。
如果您出于性能原因确实需要一个循环,则需要预分配并使用不安全的 block ,例如:
struct Pool {
strings: Vec<String>,
}
const SIZE: usize = 10;
impl Pool {
pub fn new() -> Self {
Self {
strings: Vec::with_capacity(SIZE),
}
}
pub fn some_f(&mut self) -> Vec<&str> {
let mut v: Vec<&str> = vec![];
// We've allocated 10 elements, but the loop uses 9, it's OK as long as it's not the other way around!
for i in 1..SIZE {
let string = format!("{}", i);
self.strings.push(string);
let raw = &self.strings as *const Vec<String>;
unsafe {
let last = (*raw).last().unwrap();
v.push(last);
}
}
v
}
}
一个可能的替代方案是使用 Rc
,但如果您想要单个循环的原因是性能,那么使用堆 + 引用计数的运行时成本可能是一个糟糕的权衡。无论如何,这里是代码:
use std::rc::Rc;
struct Pool {
strings: Vec<Rc<String>>,
}
impl Pool {
pub fn new() -> Self {
Self { strings: vec![] }
}
pub fn some_f(&mut self) -> Vec<Rc<String>> {
let mut v = vec![];
for i in 1..10 {
let string = format!("{}", i);
let rc = Rc::new(string);
let result = rc.clone();
self.strings.push(rc);
v.push(result);
}
v
}
}
关于rust - 在循环中发生变异时,可变借用时间过长,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64627891/
为什么禁用类型像 type t = A of int | B of string * mutable int 虽然允许此类类型: type t = A of int | B of string * i
我正在寻找一种类似结构的数据结构,我可以从中创建多个实例并具有某种类型提示而不是不可变的。 所以我有这样的东西: class ConnectionConfig(NamedTuple): nam
我需要转到引用的结构: class SearchKnot { var isWord : Bool = false var text : String = "" var to
如sec 10.4.3中所述 当控制进入执行时,执行以下步骤 功能对象F(调用者)中包含的功能代码的上下文 提供thisArg,而调用方提供argumentsList: 如
i make a game that start display Activity indicator And activity indicator bottom display UiLable wi
编辑:我在这里不断获得支持。只是为了记录,我认为这不再重要。自从我发布它以来我就不再需要它了。 我想在 Scala 中执行以下操作... def save(srcPath: String, destP
使用可变对象作为 Hashmap 键是一种不好的做法吗?当您尝试使用已修改足以更改其哈希码的键从 HashMap 中检索值时,会发生什么? 例如,给定 class Key { int a; /
如果您在Kotlin中访问List类型的Java值,则将获得(Mutable)List!类型。 例如。: Java代码: public class Example { public stati
我编写了 str 类(内置)的以下扩展,以便执行以下操作:假设我有字符串 "Ciao" ,通过做"Ciao" - "a"我想要的结果是字符串 "Cio" 。这是执行此操作的代码,并且运行良好: cla
使用可变对象作为 Hashmap 键是一种不好的做法吗?当您尝试使用已修改足以更改其哈希码的键从 HashMap 中检索值时,会发生什么? 例如,给定 class Key { int a; /
我正在为我的公司设计一个数据库来管理商业贷款。每笔贷款都可以有担保人,可以是个人或公司,在借款业务失败时作为财务支持。 我有 3 个表:Loan、Person 和 Company,它们存储明显的信息。
我使用二进制序列化从 C# 类中保存 F# 记录。一切正常: F#: type GameState = { LevelStatus : LevelStatus
import javax.swing.JOptionPane; public class HW { public static void main(String[] args) { Strin
使用 flatbuffer mutable 有多少性能损失? 是否“正确”使用 FlatBuffers 来拥有一个应该可编辑的对象/结构(即游戏状态) 在我的示例中,我现在有以下类: class Ga
std::function create_function (args...) { int x = initial_value (args...); return [x] () mut
我需要在 for 循环中找到用户输入的字符。我通常会这样做 如果(句子[i] == 'e') 但是因为在这里,'e' 将是一个单字母字符变量,我不知道如何获取要比较的值。我不能只输入 if (sent
我有一个这样的算法: let seed: Foo = ... let mut stack: Vec = Vec::new(); stack.push(&seed); while let Some(ne
这个问题可能看起来非常基础,但我很难弄清楚如何做。我有一个整数,我需要使用 for 循环来循环整数次。 首先,我尝试了—— fn main() { let number = 10; // An
如果我有以下结构: struct MyStruct { tuple: (i32, i32) }; 以及以下函数: // This will not compile fn function(&mut s
我希望在每个 session 的基础上指定列的默认值。下面的脚本不起作用,但描述了我想如何使用它。我目前使用的是 MySQL 5.5.28,但如果需要可以升级。 CREATE TABLE my_tbl
我是一名优秀的程序员,十分优秀!