我有以下内容:
enum SomeType {
VariantA(String),
VariantB(String, i32),
}
fn transform(x: SomeType) -> SomeType {
// very complicated transformation, reusing parts of x in order to produce result:
match x {
SomeType::VariantA(s) => SomeType::VariantB(s, 0),
SomeType::VariantB(s, i) => SomeType::VariantB(s, 2 * i),
}
}
fn main() {
let mut data = vec![
SomeType::VariantA("hello".to_string()),
SomeType::VariantA("bye".to_string()),
SomeType::VariantB("asdf".to_string(), 34),
];
}
我现在想在data
的每个元素上调用变换
,并将结果值存储回data
中。我可以做类似data.into_iter(). map(变换). Collection()
的事情,但这将分配一个新的Vec
。有没有一种方法可以就地执行此操作,重用data
的分配内存?Rust中曾经有Vec::map_in_place
,但它已经被删除了一段时间。
作为解决方法,我向 SomeType
添加了一个虚拟
变体,然后执行以下操作:
for x in &mut data {
let original = ::std::mem::replace(x, SomeType::Dummy);
*x = transform(original);
}
这感觉不对,我必须处理代码中其他任何地方的<code>SomeType::Dummy</code>问题,尽管它不应该在这个循环之外可见。有没有更好的方法?
您可以根据< code>take_mut或< code>replace_with板条箱编写< code>map_in_place:
fn map_in_place<T, F>(v: &mut [T], f: F)
where
F: Fn(T) -> T,
{
for e in v {
take_mut::take(e, f);
}
}
但是,如果此在提供的函数中死机,程序将完全中止;你无法从恐慌中恢复过来。
或者,您可以提供一个占位符元素,该元素在内部函数执行时位于空位置:
use std::mem;
fn map_in_place_with_placeholder<T, F>(v: &mut [T], f: F, mut placeholder: T)
where
F: Fn(T) -> T,
{
for e in v {
let mut tmp = mem::replace(e, placeholder);
tmp = f(tmp);
placeholder = mem::replace(e, tmp);
}
}
如果出现恐慌,您提供的占位符将位于恐慌状态。
最后,您可以按需生成占位符;在第一个版本中,基本上将take_mut::take
替换为。
不,通常不可能,因为每个元素的大小可能会随着映射的执行而改变(fn-transform(u8)-
即使大小相同,这也不是微不足道的。
在这种情况下,您不需要创建
虚拟变体,
因为创建空字符串
很便宜;只有 3 个指针大小的值,没有堆分配:
impl SomeType {
fn transform(&mut self) {
use SomeType::*;
let old = std::mem::replace(self, VariantA(String::new()));
// Note this line for the detailed explanation
*self = match old {
VariantA(s) => VariantB(s, 0),
VariantB(s, i) => VariantB(s, 2 * i),
};
}
}
for x in &mut data {
x.transform();
}
一个替代实现,只是替换
字符串
:
impl SomeType {
fn transform(&mut self) {
use SomeType::*;
*self = match self {
VariantA(s) => {
let s = std::mem::replace(s, String::new());
VariantB(s, 0)
}
VariantB(s, i) => {
let s = std::mem::replace(s, String::new());
VariantB(s, 2 * *i)
}
};
}
}
一般来说,是的,你必须创建一些虚拟值,用安全的代码来做这些。很多时候,可以将整个元素包装在< code>Option
中,调用< code>Option::take来达到同样的效果。
另请参阅:
请参阅这个提议的现已关闭的RFC,了解大量相关讨论。我对RFC(及其背后的复杂性)的理解是,在一段时间内,您的值将具有未定义的值,这是不安全的。如果在那一秒发生了紧急情况,那么当您的值被丢弃时,您可能会触发未定义的行为,这是一件坏事。
如果您的代码在注释行处出错,那么< code>self的值是一个具体的已知值。如果它是某个未知值,删除该字符串将尝试删除该未知值,我们又回到了c中。这就是< code>Dummy值的目的——总是存储一个已知良好的值。
你甚至暗示了这一点(我的重点):
我必须处理SomeType::D ummy
在代码中的其他地方,尽管它不应该在这个循环之外可见。
“应该”是问题所在。在恐慌期间,这个虚拟值是可见的。
另请参阅:
现在删除的 Vec::html" target="_blank">map_in_place
实现跨越了近 175 行代码,其中大部分必须处理不安全的代码,并推理为什么它实际上是安全的!一些板条箱重新实施了这一概念,并试图使其安全;你可以在Sebastian Redl的答案中看到一个例子。
第一个问题不是映射
,而是变换
。
transform
拥有其参数的所有权,而Vec
拥有其参数的所有权。任何一个都必须付出,在Vec
上戳一个洞将是一个坏主意:如果转型
恐慌怎么办?
因此,最好的解决方法是将< code>transform的签名改为:
fn transform(x: &mut SomeType) { ... }
然后你可以做:
for x in &mut data { transform(x) }
其他解决方案将是笨拙的,因为它们需要处理变换
可能会恐慌的事实。
我想转换一个
我想使用并使其直接进入给定的url,而不是从ribbon配置中获取主机。 我知道在Spring,cloud-feign默认与ribbon和eureka一起出现。 根据这个:https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-ribbon.html#spring-cloud-ribbon-without-eure
问题内容: 这是我的代码: 我对Go很陌生,但据我所知,由于我将映射值传递给而不是指向映射的指针,因此该函数应修改映射的其他变量,因此不会影响变量的值。我希望它能打印出来。我测试了另一种情况: 在这种情况下,代码的行为符合我的预期。函数中的变量不受变量中更改的影响。那么为什么地图不同? 问题答案: 正确的做法是,当您将某些内容传递给函数时,将创建一个副本。但是,映射是对基础数据结构的某种描述符。因
代码: https://play.rust-lang.org/?version=stable 返回此错误: 错误[E0277]:类型为
我对生锈编程是新手。我想用递归实现合并排序。这是我的代码: 问题是,当我试图编译它时,我得到了以下错误: 你知道我为什么会犯这个奇怪的错误吗!看来,我错过了什么。
描述 (Description) 此函数使用指定EXPR的字符串作为无符号整数的向量。 NUMBITS参数是为位向量中的每个条目保留的位数。 这必须是从1到32的2的幂。请注意,偏移量是向量结束的标记,它会计算指定的位数以查找开始。 可以使用逻辑按位运算符|,&和^来操纵向量。 语法 (Syntax) 以下是此函数的简单语法 - vec EXPR, OFFSET, BITS 返回值 (Retur