Enums 是否可以将可变引用值中的变量切换到枚举?
是否可以切换可变参考值(Enums 是否可以将可变引用值中的变量切换到枚举?,enums,rust,Enums,Rust,是否可以切换可变参考值(&mut E)中的变量,而无需对T附加限制,也无需使用不安全的代码 即,给定一个枚举: enum E<T> { VariantA(T), VariantB(T) } enum E{ 瓦里安塔(T), 瓦里安TB(T) } 正确的书写方式是什么: let x: E<???> = E::VariantA(??); change_to_variant_b(&mut x); assert_eq!(x, E::VariantB(?
&mut E
)中的变量,而无需对T
附加限制,也无需使用不安全的代码
即,给定一个枚举:
enum E<T> {
VariantA(T),
VariantB(T)
}
enum E{
瓦里安塔(T),
瓦里安TB(T)
}
正确的书写方式是什么:
let x: E<???> = E::VariantA(??);
change_to_variant_b(&mut x);
assert_eq!(x, E::VariantB(??));
设x:E=E::VariantA(??);
将_更改为_变体_b(&mut x);
断言!(x,E::VariantB(??);
我要在这里冒险说不
只需对签名稍作更改即可:
fn change_to_variant_b<T>(e: E<T>) -> E<T> {
match e {
E::VariantA(t) => E::VariantB(t),
E::VariantB(t) => E::VariantB(t),
}
}
可以使用附加边界(
默认值
,或克隆
):
fn将变量更改为变量(e:&mut e){
匹配std::mem::replace(e,e::VariantA(T::default()){
E::VariantA(t)=>E=E::VariantB(t),
E::VariantB(t)=>E=E::VariantB(t),
}
}
是否可以在可变引用的值中切换变量
因此,一般的答案是“不”。原因是这样做会使枚举处于不确定状态,这将允许访问未定义的内存,这将允许打破Rust的安全保证。作为示例,让我们假设此代码编译时没有错误:
impl<T> E<T> {
fn promote_to_b(&mut self) {
if let E::VariantA(val) = *self {
// Things happen
*self = E::VariantB(val);
}
}
}
impl{
fn将_升级到_b(&mut self){
如果让E::VariantA(val)=*self{
//事情发生了
*self=E::VariantB(val);
}
}
}
问题是,一旦将值从self
移动到val
,表示T
的内存在self
中会发生什么情况
如果我们复制了位,然后在“事情发生”中发生了恐慌,则val
和T
内部self
的析构函数将运行,但由于这些点位于相同的数据,这将导致双自由
如果我们不复制这些位,那么您就无法安全地访问“Things Aching”(事情发生)中的val
,这将是一个微不足道的有用信息
“按值”解决方案之所以有效,是因为编译器可以跟踪应该调用析构函数的人:函数本身。一旦进入函数内部,编译器就会知道哪些特定行可能需要释放该值,并在出现死机时正确调用它们
克隆
或默认
解决方案有效,因为您从未将值移出原始枚举。相反,您可以使用伪值替换原始枚举,并获得原始枚举的所有权(使用默认值
)或复制整个原始值(使用克隆
)
建议添加一种方法,以确保正确的内存语义被支持,但RFC没有被接受。
< P>作为这种特殊情况的替代,可以考虑用<代码> T < /C>和一个简单的枚举:struct Outer<T> {
val: T,
kind: Inner,
}
impl<T> Outer<T> {
fn promote_to_b(&mut self) {
self.kind.promote_to_b()
}
}
enum Inner {
VariantA,
VariantB,
}
impl Inner {
fn promote_to_b(&mut self) {
if let Inner::VariantA = *self {
*self = Inner::VariantB;
}
}
}
struct-Outer{
瓦尔:T,
种类:内部,
}
内部外部{
fn将_升级到_b(&mut self){
self.kind.promotion_to_b()
}
}
枚举内部{
瓦里纳塔,
瓦里安,
}
impl内部{
fn将_升级到_b(&mut self){
如果让内部::VariantA=*self{
*self=内部::VariantB;
}
}
}
看起来像是封闭式结构可以解决的问题。或者,将其更改为带有T
和普通enum
的struct
对您的情况有效吗?@ChrisEmerson非常有效。我只是认为变体必须有一种方法来假定其兄弟的内容,因为它直观上并不与Rust的所有权模型背道而驰。我不确定这是否与原始问题足够接近,以保证有一个答案,但如果您愿意拥有一个不包含t
的变体,并容忍“不可能”的路径通过代码,我已经编写了一个安全、稳定的change\u to\u variant\u b
版本,它使用std::mem::replace
。道具以获得清晰的答案并详细列出备选方案。我需要&mut E
,因为我在一个切片上进行可变迭代,所以我不能(?)暂时将元素移出,以使第一个解决方案生效。另外,作为一个典范解决方案,请参见@Doe:Oh,如果Chris解决方案对您有效,那么一定要使用它。在简化的示例中,总是有点难想出替代方案,因为不清楚哪些是可以更改的,哪些是不能更改的。@maybe”“解决方案”不是描述Chris评论的最佳词汇(因为它完全回避了问题)。然而,对于大多数正在寻找解决方案的访问者来说,Chris的评论可能就足够了。不可能是唯一符合OP标准的答案。@Doe:是的,Chris和我一样,都是更多的变通方法。如果你是(1)坚持使用enum
,(2)坚持使用函数签名,以及(3)坚持使用无不安全/无边界…那么您就不能这样做。
impl<T> E<T> {
fn promote_to_b(&mut self) {
if let E::VariantA(val) = *self {
// Things happen
*self = E::VariantB(val);
}
}
}
struct Outer<T> {
val: T,
kind: Inner,
}
impl<T> Outer<T> {
fn promote_to_b(&mut self) {
self.kind.promote_to_b()
}
}
enum Inner {
VariantA,
VariantB,
}
impl Inner {
fn promote_to_b(&mut self) {
if let Inner::VariantA = *self {
*self = Inner::VariantB;
}
}
}