[英]How can I easily get a reference to a value after it has been moved into a tuple-type enum variant?
I want to move a value into a tuple-type enum variant and obtain a reference to the value after it has been moved.我想将一个值移动到元组类型的枚举变体中,并在移动后获取对该值的引用。 I see how this is possible with an if let
statement, but this seems like this should be unnecessary when the particular variant is known statically.我看到了如何使用if let
语句实现这一点,但是当静态知道特定变体时,这似乎是不必要的。
Is there any way to get the reference to the moved value without requiring an if let
or match
?有没有办法在不需要if let
或match
的情况下获取对移动值的引用?
This code block is a simple illustration of my question (see below for a more challenging case):此代码块是我的问题的简单说明(请参阅下面的更具挑战性的案例):
enum Transport {
Car(u32), // horsepower
Horse(String), // name
}
fn do_something(x: &String) {
println!(x);
}
fn main() {
// Can I avoid needing this if, which is clearly redundant?
if let Transport::Horse(ref name) = Transport::Horse("daisy".into()) {
do_something(name);
}
else {
// Can never happen
}
// I tried the following, it gives:
// "error[E0005]: refutable pattern in local binding: `Car(_)` not covered"
let Transport::Horse(ref name) = Transport::Horse("daisy".into());
}
It is easy to find ways to side-step the issue in the above code, since there are no real interface requirements.在上面的代码中很容易找到回避问题的方法,因为没有真正的接口要求。 Consider instead the following example, where I am building a simple API for building trees (where each node can have n
children).请考虑以下示例,我正在构建一个简单的 API 来构建树(其中每个节点可以有n
个子节点)。 Nodes have an add_child_node
method returning a reference to the node that was added, to allow chaining of calls to quickly build deep trees.节点有一个add_child_node
方法返回对已添加节点的引用,以允许链接调用以快速构建深层树。 (It is debatable whether this is a good API, but that is irrelevant to the question). (这是否是一个好的 API 值得商榷,但这与问题无关)。 add_child_node
must return a mutable reference to the contents of an enum variant. add_child_node
必须返回对枚举变量内容的可变引用。 Is the if let
required in this example (without changing the API)?此示例中是否需要if let
(不更改 API)?
struct Node {
children: Vec<Child>,
// ...
}
enum Child {
Node(Node),
Leaf
}
impl Node {
fn add_child_node(&mut self, node: Node) -> &mut Node {
self.children.push(Child::Node(node));
// It seems like this if should be unnecessary
if let Some(&mut Child::Node(ref mut x)) = self.children.last() {
return x;
}
// Required to compile, since we must return something
unreachable!();
}
fn add_child_leaf(&mut self) {
// ...
}
}
No. You can use unreachable!()
for the else
case, and it's usually clear even without message/comment what's going on.不。您可以使用unreachable!()
来处理else
情况,即使没有消息/评论也通常很清楚发生了什么。 The compiler is also very likely to optimize the check away.编译器也很有可能优化检查。
If the variants have the same type you can implement AsRef
and use the Transport as a &str
:如果变体具有相同的类型,您可以实现AsRef
并将 Transport 用作&str
:
enum Transport {
Car(String),
Horse(String),
}
fn do_something<S: AsRef<str>>(x: &S) {
println!("{}", x.as_ref());
}
impl AsRef<str> for Transport {
fn as_ref(&self) -> &str {
match self {
Transport::Car(s) => s,
Transport::Horse(s) => s,
}
}
}
fn main() {
let transport = Transport::Horse("daisy".into());
do_something(&transport)
}
Otherwise you need to use a let if
binding as you are doing.否则,您需要像现在一样使用let if
绑定。 No need to use an else clause if you don't want to :如果您不想,则无需使用 else 子句:
if let Transport::Horse(ref name) = Transport::Horse("daisy".into()) {
do_something(name);
}
define From<Transport> for String
: From<Transport> for String
:
…
impl From<Transport> for String {
fn from(t: Transport) -> String {
match t {
Transport::Car(value) => value.to_string(),
Transport::Horse(name) => name,
}
}
}
fn do_something(x: Transport) {
println!("{}", String::from(x));
}
fn main() {
let horse = Transport::Horse("daisy".to_string());
let car = Transport::Car(150);
do_something(horse);
do_something(car);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.