我正试图在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
与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示例吗?
答案 0 :(得分:4)
您的代码有几个问题。让我们按顺序整理它们。
首先,您不能将不同类型的值分配给变量。在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();
}