Không, bạn không thể downcast Rc<Trait>
đến Rc<Concrete>
, vì các đối tượng trait như Rc<Trait>
không chứa bất kỳ thông tin nào về loại dữ liệu cụ thể mà dữ liệu thuộc về.
Dưới đây là một đoạn trích từ the official documentation áp dụng cho tất cả các đối tượng đặc điểm (&Trait
, Box<Trait>
, Rc<Trait>
):
pub struct TraitObject {
pub data: *mut(),
pub vtable: *mut(),
}
Các data
điểm trường vào struct chính nó, và các điểm vtable
lĩnh vực để tập hợp các chức năng gợi ý , một cho mỗi phương pháp của đặc điểm. Vào thời gian chạy, đó là tất cả những gì bạn có. Và đó là không đủ để tái tạo lại loại cấu trúc. (Với Rc<Trait>
, khối data
điểm cũng chứa số lượng tham chiếu mạnh và yếu, nhưng không có thông tin loại bổ sung.)
Nhưng có ít nhất 3 tùy chọn khác.
Trước tiên, bạn có thể thêm tất cả các thao tác bạn cần thực hiện trên Expression
s hoặc Condition
s vào đặc điểm AstNode
và triển khai chúng cho từng cấu trúc. Bằng cách này bạn không bao giờ cần phải gọi một phương thức không có sẵn trên đối tượng trait, bởi vì đặc điểm này chứa tất cả các phương thức bạn cần.
này cũng đòi hỏi phải thay thế hầu hết Rc<Expression>
và Rc<Condition>
thành viên trong cây với Rc<AstNode>
, vì bạn có thể không nhìn xuống Rc<AstNode>
(nhưng xem dưới đây về Any
):
enum Condition {
Equals(Rc<AstNode>, Rc<AstNode>),
LessThan(Rc<AstNode>, Rc<AstNode>),
...
}
phương pháp
Một biến thể này có thể được viết trên AstNode
mà phải mất &self
và trở về tham chiếu đến các loại bê tông khác nhau:
trait AstNode {
fn as_expression(&self) -> Option<&Expression> { None }
fn as_condition(&self) -> Option<&Condition> { None }
...
}
impl AstNode for Expression {
fn as_expression(&self) -> Option<&Expression> { Some(self) }
}
impl AstNode for Condition {
fn as_condition(&self) -> Option<&Condition> { Some(self) }
}
Thay vì downcasting Rc<AstNode>
đến Rc<Condition>
, chỉ lưu trữ dưới dạng số AstNode
và gọi điện, ví dụ: rc.as_condition().unwrap().method_on_condition()
, nếu bạn tự tin rc
thì thực tế là Rc<Condition>
.
Thứ hai, bạn có thể tạo một enum khác hợp nhất Condition
và Expression
và loại bỏ hoàn toàn các đối tượng trait. Đây là những gì tôi đã làm trong AST của trình thông dịch Scheme của riêng tôi. Với giải pháp này, không yêu cầu downcasting vì tất cả các thông tin kiểu có mặt tại thời gian biên dịch. (Cũng với giải pháp này, bạn chắc chắn phải thay thế Rc<Condition>
hoặc Rc<Expression>
nếu bạn cần để có được một Rc<Node>
ra khỏi nó.)
enum Node {
Condition(Condition),
Expression(Expression),
// you may add more here
}
impl Node {
fn children(&self) -> Vec<Rc<Node>> { ... }
}
Một lựa chọn thứ ba là sử dụng Any
, và một trong hai .downcast_ref()
hoặc Rc::downcast
(hiện chỉ trên mỗi đêm) Rc<Any>
vào loại bê tông khi cần.
Một biến thể nhẹ vào đó sẽ có thêm một phương pháp fn as_any(&self) -> &Any { self }
để AstNode
, và sau đó bạn có thể gọi Expression
phương pháp (mà phải mất &self
) bằng cách viết node.as_any().downcast_ref::<Expression>().method_on_expression()
. Nhưng hiện tại không có cách nào để (an toàn) upcast an Rc<Trait>
đến một Rc<Any>
, mặc dù không có lý do thực sự nào mà nó không thể hoạt động.
Any
là, nói đúng nhất, điều gần nhất với câu trả lời cho câu hỏi của bạn. Tôi không khuyến nghị điều này vì downcasting, hoặc cần để downcast, thường là dấu hiệu của thiết kế kém. Ngay cả trong các ngôn ngữ với kế thừa lớp, như Java, nếu bạn muốn làm cùng một loại điều (lưu trữ một loạt các nút trong một ví dụ ArrayList<Node>
), bạn phải thực hiện tất cả các hoạt động cần thiết có sẵn trên lớp cơ sở hoặc một nơi nào đó liệt kê tất cả các lớp con mà bạn có thể cần phải downcast, đó là một mô hình chống khủng khiếp. Bất cứ điều gì bạn làm ở đây với Any
sẽ có thể so sánh về độ phức tạp để chỉ thay đổi AstNode
thành một sự kiện.
tl; dr: Bạn cần lưu trữ mỗi nút của AST như một loại rằng (a) cung cấp tất cả những phương pháp bạn có thể cần phải gọi và (b) thống nhất tất cả các loại bạn có thể cần phải đặt vào một. Tùy chọn 1 sử dụng các đối tượng đặc điểm, trong khi tùy chọn 2 sử dụng enums, nhưng chúng khá giống nhau về nguyên tắc. Tùy chọn thứ ba là sử dụng Any
để cho phép downcasting.
liên quan Q & Một để đọc thêm:
Bạn đã có một vấn đề khác sắp tới, trên thực tế, bạn không thể quay trở lại 'Self' (một đối tượng trait không thể sử dụng' Self' theo giá trị), bạn cần trả về 'Rc' có thể. –
Có lý do nào tại sao 'Expression' không thực hiện đặc điểm' AstNode', điều này cho phép công văn động làm đúng không? –
Tôi nghĩ rằng thao tác AST là một sự phân tâm ở đây. Điều bạn thực sự muốn biết là liệu có thể downcast 'Rc 'thành' Rc