Tập 82: Traits V0 — Sự kiêu hãnh của Composition

Khi thiết kế DUY, chúng tôi sớm chốt loại bỏ hoàn toàn cơ chế kế thừa lớp (class inheritance) kiểu hướng đối tượng truyền thống của Java hay C++. Kế thừa là một công cụ tồi cho hệ thống: nó tạo ra các mối quan hệ cha-con chặt chẽ và cứng nhắc ở compile-time, đồng thời giấu đi memory layout thực tế của đối tượng, khiến việc tối ưu hóa căn lề bộ nhớ trở nên bất khả thi. Nhưng chúng tôi vẫn cần đa hình (polymorphism) để viết code tái sử dụng. Giải pháp là Traits V0 — hệ thống interface tĩnh dựa trên tính composition.

Sprint 139 giới thiệu Traits V0. Để ngăn chặn tình trạng xung đột định nghĩa impl (orphan rule) khi ghép các package của bên thứ ba lại với nhau, chúng tôi thiết lập quy tắc coherence tối thiểu: một lập trình viên chỉ được phép impl Trait for Struct nếu họ sở hữu Trait hoặc Struct đó trong module hiện tại.

trait Show {
  fn show(self) -> i32;
}

impl Show for Vector {
  fn show(self) -> i32 {
    print(self.len);
    return 0;
  }
}

Ở V0, chúng tôi kiên quyết trì hoãn (defer) trait objects và dynamic dispatch qua vtable. Mọi lời gọi trait method đều phải được phân giải tĩnh (static dispatch) tại thời điểm biên dịch. Điều này có nghĩa là nếu một hàm nhận một tham số kiểu trait, nó bắt buộc phải thông qua cơ chế generics và được monomorphize hoàn toàn.

Quyết định này vấp phải sự phản đối của một vài suy nghĩ quen với thế giới C# hay Java. Họ muốn sự linh hoạt của run-time interface. Họ lập luận rằng việc monomorphize toàn bộ sẽ làm phình to kích thước file nhị phân đầu ra. Nhưng câu trả lời của tôi rất rõ ràng: "Nếu anh muốn vtable, hãy viết một struct chứa các con trỏ hàm thủ công. Khi đó, chi phí bộ nhớ và gián tiếp của lời gọi sẽ nằm ngay trong code của anh. Compiler DUY sẽ không tự động làm điều đó sau lưng anh." Sự kiêu hãnh của Composition nằm ở chỗ nó bắt mọi chi phí phải hiển lộ, không cho phép ảo tưởng về hiệu năng tồn tại.


Bình luận (0)

Đang tải bình luận...