(图片不知道为啥用base64的显示不了 直接显示的字符串) 直接用文字说明吧。
中国象棋:
首先是一个棋盘Board,里面存放了所有的棋子;
抽象类Piece,里面又棋子的当前位置x和y,还有一个verify方法;
棋子类都继承了Piece,有Assistants,Bishop,Cannon,King,Knight,Pawn,Rook
用java实现的话如下:
public class Board{
private Piece[][] board = new Piece[10][9];
public Piece getPieceByCoordinate(int x, int y) {
//some code..
}
}
public abstract class Piece{
private int x;
private int y;
private boolean camp;
//判断棋子能否移动到nextX,nextY,需要当前局面,故要一个Board参数
public abstract boolean verify(int nextX, int nextY, Board board);
}
public class Assistants extends Piece{
@Override
public boolean verify(int nextX, int nextY, Board board) {
//some code...
}
}
...
但是在Rust上要实现上面的功能却颇为困难(对我来说)。我的大致实现如下
///这个用来代替Piece抽象类
trait Verify{
fn verify(next_x: i32, next_y: i32, board:Board<dyn Verify>) -> bool;
}
///棋盘
pub struct Board<T: Verify> {
//为了测试方便,先用2行1列试试
board: [[Box<T>; 1]; 2],
}
impl<T: Verify + Default+Debug> Board<T> {
pub fn new(pieces: [[Box<T>; 1]; 2]) -> Board<T> {
Board {
board: pieces
}
}
}
///车
#[derive(Default, Debug)]
pub struct Rook {
pub x: i32
}
impl Verify for Rook {
fn verify(next_x: i32, next_y: i32, board:Board<dyn Verify>) -> bool {
false
}
}
///象
#[derive(Default, Debug)]
pub struct Bishop{
pub x: i32
}
impl Verify for Bishop {
fn verify(next_x: i32, next_y: i32, board:Board<dyn Verify>) -> bool {
false
}
}
#[test]
fn test() {
let pieces:[[Box<dyn Verify>; 1]; 2] = [
// [Box::new(Bishop::default())],
[Box::new(Rook::default())],
[Box::new(Rook::default())],
];
let board = Board::new(pieces);
// println!("{:?}", board);
}
遇到了一堆问题,有
fn verify(next_x: i32, next_y: i32, board:Board<dyn Verify>) -> bool;
| ^^^^^^^^^^^^^^^^^ `board::Verify` cannot be made into an object
还有就是测试代码中
//我的本意是只要是实现了Verify的都可以放到这个二维数组中
let pieces:[[Box<dyn Verify>; 1]; 2] = [
// [Box::new(Bishop::default())],
[Box::new(Rook::default())],
[Box::new(Rook::default())],
];
//但是这里的报错和上面差不多
let pieces:[[Box<dyn Verify>; 1]; 2] = [
| ^^^^^^^^^^^^^^^^^^^^^^^^^ `board::Verify` cannot be made into an object
//如果我不写类型推断是可以编译通过的,但是里面只能放一种类型的数据 要么是Rook要么是Bishop
let pieces = [
// [Box::new(Bishop::default())],
[Box::new(Rook::default())],
[Box::new(Rook::default())],
];
所以想请问一下大家,该怎么解决这些问题呢?
1
共 13 条评论, 1 页
评论区
写评论个人认为我这种方法也是实现了多态性呀,虽然实现方式不JAVA就是了。这里统一了接口
verify
,但是根据传入的枚举类型不同,采用了不同的处理方法。这不就是多态的哲学么:对于所有继承了同一超类(Piece)的类(rook等)生成的对象(rook(x,y)),采用了了同一个接口接受,使用不同方法处理。不过我能理解你,每个人都有自己用顺手的写法,而且你这方法可能更通用些。我就随口嘴一句,哈哈。
--
👇
TideDlrow: 这确实是一种很好的解决方案,感谢。
不过我主要是想用Rust试试多态性--
👇
eweca: 我没学过java也不是学CS的,我自己来写的话我会这么写:
其实完全用不上泛型或者trait什么的,太麻烦了,棋子的数量种类是有限的,直接枚举就好了啊。
--
👇
TideDlrow: 一开始我是完全按照java中的来,但是编译时报不能确定大小,我就包了层Box,然后ide就在泛型那边建议加个dyn。最后就成了上面的样子。
感谢!!这就是我想要的ψ(`∇´)ψ
--
👇
Aya0wind:
rust里引用类型要用&Type,单独一个Type是获得所有权。你的verify只是读取棋盘,并不是棋子获得棋盘的所有权,所以要加&。然后就是trait要动态分发必须有self参数,没有self的函数就像java里的static方法一样是无法多态调用的,self就像java里的this一样,只不过java是成员方法默认有个this,rust要手动加。最后你的Board类型不需要用泛型,直接在成员那用Box就行。
这确实是一种很好的解决方案,感谢。
不过我主要是想用Rust试试多态性--
👇
eweca: 我没学过java也不是学CS的,我自己来写的话我会这么写:
其实完全用不上泛型或者trait什么的,太麻烦了,棋子的数量种类是有限的,直接枚举就好了啊。
--
👇
TideDlrow: 一开始我是完全按照java中的来,但是编译时报不能确定大小,我就包了层Box,然后ide就在泛型那边建议加个dyn。最后就成了上面的样子。
rust里引用类型要用&Type,单独一个Type是获得所有权。你的verify只是读取棋盘,并不是棋子获得棋盘的所有权,所以要加&。然后就是trait要动态分发必须有self参数,没有self的函数就像java里的static方法一样是无法多态调用的,self就像java里的this一样,只不过java是成员方法默认有个this,rust要手动加。最后你的Board类型不需要用泛型,直接在成员那用Box就行。
调试回复了下,突然多了好多评论。正如楼下所说,Enum更方便呢。
我没学过java也不是学CS的,我自己来写的话我会这么写:
其实完全用不上泛型或者trait什么的,太麻烦了,棋子的数量种类是有限的,直接枚举就好了啊。
--
👇
TideDlrow: 一开始我是完全按照java中的来,但是编译时报不能确定大小,我就包了层Box,然后ide就在泛型那边建议加个dyn。最后就成了上面的样子。
参考这里:
https://doc.rust-lang.org/reference/items/traits.html#supertraits
你可以这样定义trait:
感觉用 enum 会比 trait 好些点
一开始我是完全按照java中的来,但是编译时报不能确定大小,我就包了层Box,然后ide就在泛型那边建议加个dyn。最后就成了上面的样子。
个人理解,实际上你就是想要让Board里的棋子只有实现了verify的,这样你才能用verify判断那个棋子可以不可以移动到指定位置。但是实际上这个只要用到trait bound的功能就可以了。跟dyn这种动态派发的机制貌似没啥关系吧?
业余玩家,说错勿喷。如果你只是想要struct board只接受实现了trait的泛型T,那么trait那边这么写吧:
混淆了 generic 和 trait object,下面这段代码在编译期就实例化了的。
要动态分发的话,用
dyn Verify
(你后面不都用到了么)