Dynamic typing trong Rust với std::any::Any
Xét một tình huống thường gặp trong lập trình: Giả sử ta có một interface kiểu Shape
, và 2 class là Rectangle
, Circle
cùng implement interface Shape
, sau đó ta tạo một danh sách tên là shapes
để chứa tất cả các đối tượng có implement Shape
.
Trong Rust code sẽ nhìn như thế này:
pub trait Shape {} pub struct Rectangle {}
impl Shape for Rectangle {} pub struct Circle {}
impl Shape for Circle {} fn main() { let shapes: Vec<Box<dyn Shape>> = vec![ Box::new(Rectangle {}), Box::new(Circle {}), ];
}
Trong quá trình làm việc với mảng shapes
, có lúc chúng ta muốn lấy một giá trị ra và cast nó về kiểu Rectangle
hoặc Circle
để sử dụng, thường thì chúng ta sẽ làm như này:
let rect: &Rectangle = shapes.get(0).unwrap().as_ref();
Xong rồi sẽ bị Rust chửi vào mặt:
error[E0308]: mismatched types --> src/main.rs:15:28 |
15 | let rect: &Rectangle = shapes.get(0).unwrap().as_ref(); | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Rectangle`, found trait object `dyn Shape` | | | expected due to this | = note: expected reference `&Rectangle` found reference `&dyn Shape`
Lỗi vì shapes
là một vector chứa các object kiểu dyn Shape
, nên khi dùng hàm get()
để lấy một phần tử ra, phần tử đó sẽ mang kiểu dyn Shape
.
Để có thể chuyển một object kiểu dyn Shape
thành Rectangle
, chúng ta có thể implement trait std::any::Any
cho kiểu Rectangle
.
pub trait Shape { fn as_any(&self) -> &dyn std::any::Any;
} pub struct Rectangle {}
impl Shape for Rectangle { fn as_any(&self) -> &dyn std::any::Any { self }
}
Từ bây giờ, chúng ta có thể gọi hàm .as_any()
để chuyển một Shape
về kiểu Any
, rồi dùng hàm downcast_ref
của Any
để cast nó về kiểu mong muốn:
let shape: &dyn Shape = shapes.get(0).unwrap().as_ref();
let rect: &Rectangle = shape.as_any().downcast_ref().unwrap();