2016-09-12 20 views
11

Tôi đang cố gắng để xác định một cấu trúc có thể hoạt động như một iterator cho một Vec được tổ chức trong vòng một RefCell:Encapsulating tuần tự khởi tạo trạng thái với tự tài liệu tham khảo trong Rust struct

use std::slice::Iter; 
use std::cell::Ref; 
use std::cell::RefCell; 

struct HoldsVecInRefCell { 
    vec_in_refcell: RefCell<Vec<i32>>, 
} 

// TODO: struct HoldsVecInRefCellIter implementing Iterator ... 

impl HoldsVecInRefCell { 
    fn new() -> HoldsVecInRefCell { 
     HoldsVecInRefCell { vec_in_refcell: RefCell::new(Vec::new()) } 
    } 

    fn add_int(&self, i: i32) { 
     self.vec_in_refcell.borrow_mut().push(i); 
    } 

    fn iter(&self) -> HoldsVecInRefCellIter { 
     // TODO ... 
    } 
} 

fn main() { 
    let holds_vec = HoldsVecInRefCell::new(); 
    holds_vec.add_int(1); 
    holds_vec.add_int(2); 
    holds_vec.add_int(3); 

    let mut vec_iter = holds_vec.iter(); // Under the hood: run-time borrow check 

    for i in vec_iter { 
     println!("{}", i); 
    } 
} 

Để so sánh, có thể vec_iter được khởi tạo trong dòng trong main() như sau (cố ý tiết):

// Elided: lifetime parameter of Ref 
let vec_ref: Ref<Vec<i32>> = holds_vec.vec_in_refcell.borrow(); 
// Elided: lifetime parameter of Iter 
let mut vec_iter: Iter<i32> = vec_ref.iter(); 

có cách nào để xác định một cấu trúc thực hiện Iterator chứa cả Ref (để giữ số không thay đổi) và Iter (để duy trì trạng thái trình lặp cho next(), thay vì cuộn trình lặp của riêng mình cho Vec hoặc bất kỳ vùng chứa nào khác), khi thứ hai bắt nguồn từ (và giữ tham chiếu thu được từ) Đầu tiên?

Tôi đã thử một số cách tiếp cận để triển khai điều này và tất cả đều hoạt động với người kiểm tra vay. Nếu tôi đặt cả hai miếng của nhà nước thành viên struct như trần, như

struct HoldsVecInRefCellIter<'a> { 
    vec_ref: Ref<'a, Vec<i32>>, 
    vec_iter: Iter<'a, i32>, 
} 

sau đó tôi không thể khởi tạo cả hai lĩnh vực cùng một lúc với HoldsVecInRefCellIter { ... } cú pháp (xem ví dụ Does Rust have syntax for initializing a struct field with an earlier field?). Nếu tôi cố gắng shunt khởi tuần tự với một cấu trúc giống như

struct HoldsVecInRefCellIter<'a> { 
    vec_ref: Ref<'a, Vec<i32>>, 
    vec_iter: Option<Iter<'a, i32>>, 
} 

// ... 

impl HoldsVecInRefCell { 
    // ... 

    fn iter(&self) -> HoldsVecInRefCellIter { 
     let mut new_iter = HoldsVecInRefCellIter { vec_ref: self.vec_in_refcell.borrow(), vec_iter: None }; 
     new_iter.vec_iter = new_iter.vec_ref.iter(); 
     new_iter 
    } 
} 

sau đó tôi phải chịu có thể thay đổi tự mượn của struct mà ngăn trở nó từ iter(). Điều này tự vay của một cấu trúc cũng có thể xảy ra nếu bạn cố gắng để lưu trữ một tham chiếu đến một phần của một cấu trúc trong cấu trúc chính nó (Why can't I store a value and a reference to that value in the same struct?), mà sẽ ngăn chặn một cách an toàn di chuyển trường hợp của cấu trúc. Bằng cách so sánh, nó có vẻ giống như một cấu trúc như HoldsVecInRefCellIter, nếu bạn có thể hoàn thành khởi tạo, sẽ làm điều chính xác khi được di chuyển, vì tất cả các tham chiếu bên trong là dữ liệu ở nơi khác ngoài cấu trúc này. Có một số thủ thuật để tránh tự tạo tài liệu tham khảo bằng cách sử dụng Rc (xem ví dụ tại https://internals.rust-lang.org/t/self-referencing-structs/418/3), nhưng tôi không thấy cách chúng có thể được áp dụng nếu bạn muốn lưu trữ cấu trúc Iterator hiện tại được triển khai để giữ tham chiếu trực tiếp vào vùng chứa bên dưới, không phải là Rc.

Là một newbie Rust đến từ C++, điều này giống như một vấn đề thường xuất hiện ("Tôi có một số logic khởi tạo phức tạp trong một khối mã, và tôi muốn trừu tượng hóa logic đó và giữ trạng thái kết quả trong một cấu trúc để sử dụng ").

Câu hỏi liên quan:Returning iterator of a Vec in a RefCell

+0

khởi Complex là phổ biến - đó là thường giải quyết bằng mô hình Builder; đó không phải là vấn đề ở đây. Trình lặp của một slice hy vọng có thể giữ một tham chiếu đến slice cho toàn bộ thời gian nó tồn tại; trong thực tế bạn [không thể viết một trình vòng lặp tạo ra các tham chiếu đến chính nó] (http://stackoverflow.com/questions/25702909/can-i-write-an-iterator-that-yields-a-reference-into-itself). – Shepmaster

+0

Bạn có thể thêm phương thức 'fn mượn (& self) -> Ref >' mà bạn có thể lưu trữ và sau đó gọi 'iter', hoặc bạn có thể chấp nhận một đóng được cung cấp một trình lặp:' fn iter )> (& self, f: F) ' – Shepmaster

+0

Để làm rõ: ý định của tôi là cho trình vòng lặp để trả về giá trị, chứ không phải tham chiếu (' i32' trong mã ví dụ). Thách thức của tôi là khởi tạo một cấu trúc lặp có thể tạo ra các giá trị bằng cách lưu trữ một 'Iterator' cho vùng chứa bên dưới. Đối với ví dụ này, 'HoldsVecInRefCellIter' có thể chỉ giữ' Ref > 'và một chỉ mục vào vectơ, nhưng đối với' Ref > 'Tôi tưởng tượng bạn muốn' Iterator' của container. –

Trả lời

6

Chúng tôi sẽ phải gian lận và nói dối về tuổi thọ.

use std::mem; 

struct HoldsVecInRefCellIter<'a> { 
    vec_ref: Ref<'a, Vec<i32>>, 
    vec_iter: Iter<'a, i32>, // 'a is a lie! 
} 

impl HoldsVecInRefCell { 
    fn iter(&self) -> HoldsVecInRefCellIter { 
     unsafe { 
      let vec_ref = self.vec_in_refcell.borrow(); 
      // transmute changes the lifetime parameter on the Iter 
      let vec_iter = mem::transmute(vec_ref.iter()); 
      HoldsVecInRefCellIter { vec_ref: vec_ref, vec_iter: vec_iter } 
     } 
    } 
} 

impl<'a> Iterator for HoldsVecInRefCellIter<'a> { 
    type Item = i32; 

    fn next(&mut self) -> Option<Self::Item> { 
     self.vec_iter.next().cloned() 
    } 
} 

chỉ này hoạt động vì Iter không mất hiệu lực bằng cách di chuyển Ref, như Ref điểm đến VecIter điểm để lưu trữ các 's Vec, không phải trên Ref riêng của mình.

Tuy nhiên, điều này cũng cho phép bạn di chuyển vec_iter ra khỏi số HoldsVecInRefCellIter; nếu bạn trích xuất vec_iter và thả vec_ref, thì khoản vay sẽ được giải phóng và Iter có thể bị vô hiệu mà không có Rust đưa ra lỗi biên dịch ('a là thời gian của RefCell). Với đóng gói thích hợp, bạn có thể giữ nội dung của cấu trúc riêng tư và tránh người dùng thực hiện thao tác không an toàn này.

Bằng cách này, chúng ta có thể chỉ cũng xác định iterator trở về tham khảo:

impl<'a> Iterator for HoldsVecInRefCellIter<'a> { 
    type Item = &'a i32; 

    fn next(&mut self) -> Option<Self::Item> { 
     self.vec_iter.next() 
    } 
} 
Các vấn đề liên quan