2015-01-11 17 views
10

Trong Rob Pike's talk trên quét từ vựng ở Gò, ông định nghĩa một hàm kiểu stateFn trả về stateFn khác, như vậy:Recursive chức năng loại

type stateFn func() stateFn 

Trong một nỗ lực để làm điều gì đó tương tự ở Rust, tôi đã cố gắng này:

type stateFn = fn() -> stateFn; 

nhưng trình biên dịch than phiền "loại đệ quy bất hợp pháp; chèn một enum hoặc cấu trúc trong chu kỳ, nếu điều này là mong muốn".

Tôi có thể làm điều này trong Rust, và nếu có, làm cách nào?

Trả lời

15

Bạn có thể bọc loại chức năng theo loại danh nghĩa (ví dụ: cấu trúc hoặc enum). Đây thực sự là những gì các mã Go được thực hiện: type T U định nghĩa một mới, khác biệt loại T đó không phải là trực tiếp hoán đổi với U, trong khi Rust type chỉ là một bí danh, như type trong Haskell và typedef trong C.

Vì vậy, người ta có thể viết:

struct StateFn(fn() -> Option<StateFn>); 

hoặc

struct StateFn { 
    f: fn() -> Option<StateFn> 
} 

(tôi đã có thêm Option vì Go 01.có thể con số không, trong khi Rust loại bỏ nullability theo mặc định, làm cho nó opt-in.)

Điều đó nói rằng, tôi nghi ngờ rằng func là một đóng ở Gò (có thể lưu trữ một số trạng thái nội bộ), trong khi fn ở Rust chỉ là một là con trỏ hàm (không có trạng thái nào cả), vì vậy bạn có thể muốn sử dụng một đóng trong Rust nữa. Người ta có thể làm điều này bằng cách thay thế fn() -> Option<StateFn> bằng một số Box< Fn() -> Option<StateFn>> và tạo nó bằng Box::new(move || { /* code here */ }).

Bạn cũng có thể sử dụng FnMut thay vì Fn mang lại cho bạn sự linh hoạt hơn hoặc thậm chí FnOnce đại diện cho việc đóng cửa chỉ có thể được gọi một lần. Mỗi một trong số những nơi này tiếp tục hạn chế nhiều hơn về người gọi, nhưng cho phép đóng cửa chính nó linh hoạt hơn liên tục. (Tuy nhiên, "an toàn đối tượng" mối quan tâm có nghĩa là Box<FnOnce> không hoạt động vào lúc này, "Purging proc" có thêm chi tiết và một công việc xung quanh.)

struct StateFn { 
    f: Box<FnMut() -> Option<StateFn>> 
} 

Vòng lặp phân tích cú pháp cho bất kỳ tình huống này có thể trông giống như:

let mut state_fn = Some(initial_fn); 
while let Some(mut f) = state_fn { 
    state_fn = (*f.f)() 
} 
+0

Fn đặt những hạn chế nhất trên người gọi (không được biến đổi môi trường) và FnOnce thậm chí có thể tiêu thụ/di chuyển môi trường. FnOnce được triển khai cho FnMut và FnMut được triển khai cho Fn. Tôi nghĩ FnOnce phù hợp nhất ở đây. –

+1

Không, * người gọi * có các giới hạn ít nhất đối với 'Fn': ví dụ: họ có thể gọi nó thông qua một '& Fn()', nhưng không phải 'FnMut' hoặc' FnOnce' có thể được gọi ở đó, trong khi 'FnOnce' phải được gọi là giá trị và chỉ có thể được gọi một lần (hạn chế hơn nhiều) . Mặt khác, 'Fn' đặt những hạn chế nhất trên * callee * (bản thân đóng), trong khi' FnOnce' là hạn chế nhất cho việc đóng (đó là phần thân của một đóng cửa 'FnOnce' có tính linh hoạt nhất). Như câu trả lời mô tả, 'FnOnce' là tốt nhất, nhưng có một số sự tinh tế xung quanh các đối tượng đặc điểm có nghĩa là nó không hoạt động trực tiếp. – huon

+0

Đúng, bạn nói đúng. Tôi nghĩ về một hàm nhận FnOnce/FnMut/Fn làm đối số. –

Các vấn đề liên quan