Dynamic typing trong Rust với std::any::Any

801

Bài viết được sự cho phép của tác giả Huy Trần

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à RectangleCircle 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.

  Tại sao team Discord chuyển từ Go sang Rust?
  Hướng dẫn và sử dụng jquery plugin typing để giả hiệu ứng gõ văn bản

Xem thêm tuyển dụng C# lương cao trên TopDev

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();
Bài viết gốc được đăng tải tại thefullsnack.com
Có thể bạn quan tâm:
Xem thêm công việc IT hấp dẫn trên TopDev