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
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
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
Để 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. –