将Option<&T>转换为Option<T>

5

我有一个针对整数执行此操作的函数:

fn some_to_value(src: Option<&int>) -> Option<int> {
    match src {
        Some(x) => Some(*x),
        None => None
    }
}

我希望让它通用化(并仍然在调用者的级别使用 "int")。如果 T 的实例被复制,那对我来说也没问题。所以我试着这样做:

fn some_to_value<T>(src: Option<&T>) -> Option<T> {
    match src {
        Some(x) => Some(*x),
        None => None
    }
}

I get:

error: cannot move out of dereference of `&`-pointer
Some(x) => Some(*x),
                ^~

我不理解为什么它失败了(我是初学者)。
一些背景:我复制了一个选项,因为我意识到,在“HashMap”上执行“查找”后,只要“查找”的返回值(包含对映射项的引用的选项)存在,映射将是不可变的。
4个回答

4
Rust语言有一个强大的所有权概念,这导致了"move vs. copy"的情况(我将在本文中使用该答案的术语)。共享引用&T通常是内存中T的只读视图。其中一个重要属性是您不允许使T无效: &T必须始终指向有效的T实例。 当您编写*x时,您正在尝试通过值移动T,由于T是无界泛型,编译器必须假设最坏的情况:类型T不是Copy,因此必须移动所有权,即按值使用(也称字节复制)不是语义复制,这意味着源不能继续使用(请参见我的链接答案以获取更多解释)。移动所有权会使源代码无效...但源代码位于&T中,因此使其无效是非法的!

*x适用于int,因为它是Copy,所以按值使用(即字节复制)与语义复制相同: &int未被使无效。


谢谢,现在我明白了错误信息中的“移出”部分。作为一个初学者,我预期会有与复制相关的错误,这就是我感到困惑的原因。 - Fabimaru

3

如果你想允许T被复制,你需要告诉编译器。

你可以将T的类型限制为那些属于Copy种类的类型:

可以通过简单复制位(即memcpy)进行复制的类型。

fn some_to_value<T: Copy>(src: Option<&T>) -> Option<T> {
    match src {
        Some(x) => Some(*x),
        None => None
    }
}

更普遍地,将 T 限制为实现了 Clone 特性的类型:

fn some_to_value<T: Clone>(src: Option<&T>) -> Option<T> {
    match src {
        Some(x) => Some(x.clone()),
        None => None
    }
}

2
顺便提一下,第一个可以写成.map(|x| *x).map(|&x| x),第二个可以写成.map(|x| x.clone()),不需要整个函数。 - Chris Morgan
谢谢,直接使用“map”可以简洁地摆脱find返回的Option并使哈希表不可变。 - Fabimaru

2
Rust 1.0.0引入了Option<&T>::cloned(),专门用于此目的(您可以摆脱像some_to_value这样的实用程序包装器)。摘自文档:

通过克隆选项内容将Option<&T>映射到Option<T>

Rust 1.26还引入了Option<&mut T>::cloned()
Rust 1.35.0引入了Option<&T>::copied(),用于限制仅可复制类型;请参见宣布1.35博客文章

感谢您提到“copied”,我已将其添加到链接问题的答案中。 - trent

1
Some(x) => Some(*x)

这段代码将x按值传递。在你的第一个函数中,x是一个int类型。像int这样的基础数据类型实现了Copy trait,这意味着它们在按值传递时会自动复制。不实现Copy trait的类型则会被移动。

你的泛型函数并不能保证T会实现Copy。如果编译器允许这个函数编译通过,你将得到一个函数,它会解引用x,然后把底层数据复制或移动到输出Option中。然而,移动会使原始内存位置无效!

你的泛型函数将使用Copy约束来进行操作: some_to_value<T: Copy> 保证T将实现Copy。

你可能希望该函数能处理更多类型,因此你需要使用Clone trait。match语句也可以写成一个map函数,两者的功能完全相同。

fn some_to_value<T: Clone>(src: Option<&T>) -> Option<T> {
    src.map(|num| num.clone())
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接