rust - 如何将不同的类实例分配给 Rust 中的变量?

标签 rust

<分区>

我正在尝试用 Rust 创建一个简单的程序(用于教育目的)。目前,我主要使用 Java 等经典 OOP 语言进行开发,所以我知道我可能无法在 Rust 中实现同样的事情。

我试图通过根据外部(用户触发的)输入初始化变量然后调用此对象实例上的方法来避免重复代码。

我搜索了一段时间的答案,但我无法为我的问题找到明确的答案。

为了说明我的问题,我用 Java 编写了以下几行:

interface Command {
    String getName();
}

class FirstCommand implements Command {
    @Override
    public String getName() {
        return "First command";
    }
}

class SecondCommand implements Command {
    @Override
    public String getName() {
        return "Second command";
    }
}

public class Test {
    public static void main(String[] argv) {
        Command cmd;
        if (argv.length > 10) {
            cmd = new SecondCommand();
        } else {
            cmd = new FirstCommand();
        }
        System.out.println(cmd.getName());
    }
}

这与我想在 Rust 中实现的基本相同。据我所知,traits 是 Rust 等同于 Java 中的 interfaces。所以我尝试在 Rust 中做同样的事情:

use std::env;

struct FirstCommand {}
struct SecondCommand {}

trait Command {
    fn get_name() -> &'static str;
}

impl FirstCommand {
    fn new() -> FirstCommand {
        FirstCommand {}
    }
}

impl Command for FirstCommand {
    fn get_name() -> &'static str {
        "First command"
    }
}

impl SecondCommand {
    fn new() -> SecondCommand {
        SecondCommand {}
    }
}

impl Command for SecondCommand {
    fn get_name() -> &'static str {
        "Second command"
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();

    let cmd: Command = if args.len() > 10 {
        FirstCommand::new()
    } else {
        SecondCommand::new()
    };

    cmd.get_name()
}

如果我现在正在尝试编译代码。我收到以下错误消息:

38 |     let cmd: Command = if args.len() > 10 {
   |              ^^^^^^^ the trait `Command` cannot be made into an object

我尝试了同样的方法,但没有为 cmd 明确定义类型。这导致

38 |       let cmd = if args.len() > 10 {
   |  _______________-
39 | |         FirstCommand::new()
   | |         ------------------- expected because of this
40 | |     } else {
41 | |         SecondCommand::new()
   | |         ^^^^^^^^^^^^^^^^^^^^ expected struct `FirstCommand`, found struct `SecondCommand`
42 | |     };
   | |_____- if and else have incompatible types

有人可以提示我如何在 Rust 中实现 Java 示例吗?

最佳答案

您的代码存在多个问题。让我们按顺序进行。

首先,您不能将不同类型的值分配给一个变量。在 Java 中它是有效的,因为在 Java 中(几乎)所有东西都是堆分配的引用,但 Rust 区分值和引用。所以你需要明确地告诉编译器你想要堆分配的引用。在 Rust 中,这是通过 Box 完成的:

let cmd: Box<dyn Command> = if args.len() > 10 {
    Box::new (FirstCommand::new())
} else {
    Box::new (SecondCommand::new())
};

现在您的代码遇到了第二个问题,这是@French Boiethios 指出的重复问题:

error[E0038]: the trait `Command` cannot be made into an object
  --> src/main.rs:37:14
   |
37 |     let cmd: Box<dyn Command> = if args.len() > 10 {
   |              ^^^^^^^^^^^^^^^^ the trait `Command` cannot be made into an object
   |
   = note: method `get_name` has no receiver

在 Java 中,每个非静态方法都有一个名为 this 的隐式参数,它是对该方法应该运行的实例的引用。在 Rust 中,您需要将该引用显式声明为 &self:

fn get_name (&self) -> &'static str;

此时您遇到了最后一个错误:代码末尾缺少一个分号。

最终工作代码:

use std::env;

struct FirstCommand {}
struct SecondCommand {}

trait Command {
    fn get_name (&self) -> &'static str;
}

impl FirstCommand {
    fn new() -> FirstCommand {
        FirstCommand {}
    }
}

impl Command for FirstCommand {
    fn get_name (&self) -> &'static str {
        "First command"
    }
}

impl SecondCommand {
    fn new() -> SecondCommand {
        SecondCommand {}
    }
}

impl Command for SecondCommand {
    fn get_name (&self) -> &'static str {
        "Second command"
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();

    let cmd: Box<dyn Command> = if args.len() > 10 {
        Box::new (FirstCommand::new())
    } else {
        Box::new (SecondCommand::new())
    };

    cmd.get_name();
}

playground

关于rust - 如何将不同的类实例分配给 Rust 中的变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56004812/

相关文章:

unix - 我如何从 Rust 中的特定原始文件描述符中读取?

Rust:从 Slice 的基准内置排序与编译排序代码相比有 x16 差异?

generics - 无法推断默认泛型类型参数

rust - 在拥有盒装特征的结构上实现 Deref

rust - 如何从 std::io::Bytes 转换为 &[u8]

rust - 如何制作一个也跳过面向行的注释的 nom 空白解析器?

rust - 如何在循环中生成异步方法?

rust - 从现有系列和 map 值创建新系列

rust - 如何注释此 Rust/Calloop 回调代码的生命周期?

rust - Rust 中的 "Subclassing"特征