Khi nào thì thích hợp để sử dụng kiểu liên kết so với kiểu chung?


108

Trong câu hỏi này , một vấn đề nảy sinh có thể được giải quyết bằng cách thay đổi nỗ lực sử dụng tham số kiểu chung thành một kiểu liên kết. Điều đó đã đặt ra câu hỏi "Tại sao một kiểu liên kết lại thích hợp hơn ở đây?", Khiến tôi muốn biết thêm.

Các RFC đó giới thiệu các loại liên quan nói:

RFC này làm rõ sự phù hợp đặc điểm bằng cách:

  • Xử lý tất cả các tham số kiểu đặc điểm làm kiểu đầu vào
  • Cung cấp các loại liên kết, là các loại đầu ra .

RFC sử dụng cấu trúc đồ thị làm ví dụ thúc đẩy và điều này cũng được sử dụng trong tài liệu , nhưng tôi thừa nhận là không đánh giá cao đầy đủ các lợi ích của phiên bản kiểu được liên kết so với phiên bản kiểu tham số hóa. Điều chính yếu là distancephương pháp không cần quan tâm đến Edgekiểu. Điều này là tốt, nhưng có vẻ hơi nông cạn về một lý do vì có các loại liên kết.

Tôi thấy các kiểu được liên kết khá trực quan để sử dụng trong thực tế, nhưng tôi thấy mình đang gặp khó khăn khi quyết định vị trí và thời điểm nên sử dụng chúng trong API của riêng mình.

Khi viết mã, khi nào tôi nên chọn một kiểu liên kết thay vì một tham số kiểu chung và khi nào tôi nên làm ngược lại?

Câu trả lời:


75

Điều này hiện được đề cập đến trong ấn bản thứ hai của Ngôn ngữ lập trình Rust . Tuy nhiên, chúng ta hãy đi sâu vào một chút bổ sung.

Hãy để chúng tôi bắt đầu với một ví dụ đơn giản hơn.

Sử dụng phương pháp tính trạng khi nào là thích hợp?

Có nhiều cách để cung cấp ràng buộc muộn :

trait MyTrait {
    fn hello_word(&self) -> String;
}

Hoặc là:

struct MyTrait<T> {
    t: T,
    hello_world: fn(&T) -> String,
}

impl<T> MyTrait<T> {
    fn new(t: T, hello_world: fn(&T) -> String) -> MyTrait<T>;

    fn hello_world(&self) -> String {
        (self.hello_world)(self.t)
    }
}

Bỏ qua bất kỳ chiến lược triển khai / hiệu suất nào, cả hai đoạn trích trên đều cho phép người dùng chỉ định một cách năng động cách thức hello_worldhoạt động.

Một sự khác biệt (ngữ nghĩa) là traitbảo đảm thực hiện rằng đối với một loại nhất định Tthực hiện trait, hello_worldsẽ luôn luôn có hành vi tương tự trong khi structthực hiện phép có hành vi khác nhau trên cơ sở mỗi ví dụ.

Việc sử dụng một phương pháp có phù hợp hay không phụ thuộc vào usecase!

Khi nào thì thích hợp để sử dụng kiểu liên kết?

Tương tự như các traitphương thức ở trên, một kiểu liên kết là một dạng ràng buộc muộn (mặc dù nó xảy ra khi biên dịch), cho phép người dùng traitchỉ định kiểu thay thế cho một phiên bản nhất định. Đó không phải là cách duy nhất (vì vậy câu hỏi):

trait MyTrait {
    type Return;
    fn hello_world(&self) -> Self::Return;
}

Hoặc là:

trait MyTrait<Return> {
    fn hello_world(&Self) -> Return;
}

Tương đương với ràng buộc muộn của các phương pháp ở trên:

  • cái đầu tiên thực thi rằng đối với một cái nhất định Selfcó một cái Returnliên kết
  • thứ hai, thay vào đó, cho phép thực hiện MyTraitcho Selfcho nhiềuReturn

Hình thức nào phù hợp hơn tùy thuộc vào việc nó có hợp lý để thực thi unicity hay không. Ví dụ:

  • Deref sử dụng một kiểu được liên kết bởi vì nếu không có unicity, trình biên dịch sẽ phát điên khi suy luận
  • Add sử dụng kiểu được liên kết bởi vì tác giả của nó nghĩ rằng với hai đối số sẽ có kiểu trả về logic

Như bạn có thể thấy, mặc dù Dereflà một usecase rõ ràng (hạn chế kỹ thuật), trường hợp của Addnó ít rõ ràng hơn: có lẽ nó sẽ có ý nghĩa nếu i32 + i32nhường một trong hai i32hoặc Complex<i32>tùy thuộc vào ngữ cảnh? Tuy nhiên, tác giả đã thực hiện phán đoán của họ và quyết định rằng việc nạp chồng kiểu trả về cho các phép bổ sung là không cần thiết.

Lập trường cá nhân của tôi là không có câu trả lời đúng. Tuy nhiên, ngoài đối số unicity, tôi sẽ đề cập rằng các kiểu liên kết làm cho việc sử dụng đặc điểm dễ dàng hơn vì chúng giảm số lượng các tham số phải được chỉ định, vì vậy trong trường hợp lợi ích của sự linh hoạt của việc sử dụng một tham số đặc điểm thông thường không rõ ràng, tôi đề xuất bắt đầu với một loại liên kết.


4
Hãy để tôi cố gắng đơn giản hóa một chút: trait/struct MyTrait/MyStructcho phép chính xác một impl MyTrait forhoặc impl MyStruct. trait MyTrait<Return>cho phép nhiều impls vì nó chung chung. Returncó thể là bất kỳ loại nào. Cấu trúc chung giống nhau.
Paul-Sebastian Manole

2
Tôi tìm thấy câu trả lời của bạn dễ dàng hơn nhiều để hiểu hơn so với một trong "Rust Lập trình ngôn ngữ"
drojf

"cái đầu tiên thực thi rằng đối với một Bản thân nhất định có một Sự trở lại duy nhất được liên kết". Điều này đúng theo nghĩa trực tiếp, nhưng tất nhiên người ta có thể khắc phục hạn chế này bằng cách phân lớp con với một đặc điểm chung. Có lẽ tính duy nhất chỉ có thể là một gợi ý, và không được thực thi
joel

36

Các kiểu liên kết là một cơ chế nhóm , vì vậy chúng nên được sử dụng khi nhóm các kiểu với nhau có ý nghĩa.

Các Graphđặc điểm giới thiệu trong tài liệu là một ví dụ về điều này. Bạn muốn một Graphloại chung chung, nhưng khi bạn có một loại cụ thể Graph, bạn sẽ không muốn các loại Nodehoặc Edgecác loại khác nhau nữa. Một người cụ thể Graphsẽ không muốn thay đổi các kiểu đó trong một lần triển khai và trên thực tế, muốn chúng luôn giống nhau. Chúng được nhóm lại với nhau, hoặc thậm chí người ta có thể nói là liên kết .


4
Tôi đã mất một thời gian để hiểu. Đối với tôi, nó trông giống như việc xác định nhiều loại cùng một lúc: Edge và Node không có ý nghĩa gì ngoài biểu đồ.
tafia
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.