回答編集履歴

2

コメントでのご指摘を受けて回答を訂正しました。また、別の解決法を追記しました。

2022/10/05 09:01

投稿

tatsuya6502
tatsuya6502

スコア2035

test CHANGED
@@ -1,10 +1,12 @@
1
1
  > クロージャ間で変数を共有できない場合にCellを使うのは適当か
2
2
 
3
- `Cell`は実行時に僅かなオーバーヘッドがあるのと、借用ルールに違反した場合はコンパイルエラーではなく実行時エラー(panic)になるので、もし使わないですむなら使わないほうがいいです。
3
+ `Cell`は実行時に僅かなオーバーヘッドがあるのと、 ~~借用ルールに違反した場合はコンパイルエラーではなく実行時エラー(panic)になるので、~~ もし使わないですむなら使わないほうがいいです。(**2022年10月5日 訂正**:コメント欄でご指摘いただいたとおり`Cell`がpanicするというのは誤りでした。`Cell`はpanicしないのが正しいです。`RefCell`と混同してしまいました)
4
4
 
5
5
  しかし、今回の質問のようなコードで変数を共有するには`Cell`しかなさそうですので、`Cell`を使うのは適当だと思います。
6
6
 
7
- 余談ですが、ご質問のコードで試したところ、片方のクロージャーを構造体で置き換え、その構造体に`Iterator`を実装することで、`Cell`を使わずに同じ機能を実現できました。簡略化前のコードに適用できるかはわかりませんが、参考になるかもしれません。
7
+ 余談ですが、ご質問のコードで試したところ、片方のクロージャーを構造体で置き換え、その構造体に`Iterator`を実装することで、 ~~`Cell`を使わずに同じ機能を実現できました。簡略化前のコードに適用できるかはわかりませんが、参考になるかもしれません。~~
8
+
9
+ **2022年10月5日 訂正**:コメント欄でご指摘いただいたとおり、うまく動く理由は`MyIter::next`内で`collect`しているためでした。collectせず、`Iterator::Item`を`impl Iterator`にすると、おっしゃる通り、inspectのクロージャーが `&mut next_start` をキャプチャ(捕捉)しようとするので、元のコードと同じ所有権がらみのエラーになってしまいました。
8
10
 
9
11
  ```rust
10
12
  #[derive(Debug, Clone, PartialEq, Eq)]
@@ -91,3 +93,119 @@
91
93
  }
92
94
  ```
93
95
 
96
+ **2022年10月5日 追記**
97
+
98
+ 所有権の問題を回避するために、`MyIter::next`がイテレーター(`impl Iterator<Item=(i32, Line2)>`)を返すのではなく、`(i32, Line2)`を返す実装にしてみました。
99
+
100
+ ただ、ここまでするなら、`MyIter`をイテレーターにして`main`で`collect`するよりも、イテレーターにせずに`Vec<(i32, Line2)>`を返すメソッドを作る方が簡単かもしれません。
101
+
102
+ ```rust
103
+ #[derive(Debug, Clone, PartialEq, Eq)]
104
+ enum Line2 {
105
+ A,
106
+ B,
107
+ }
108
+
109
+ // クロージャーとiter::from_fn()で作っていたイテレーターを構造体で置き換える
110
+ struct MyIter<I1, F, I2> {
111
+ input_lines_iter: I1,
112
+ process_line: F,
113
+ processed_lines_iter: Option<I2>,
114
+ next_start: i32,
115
+ }
116
+
117
+ impl<I1, F, I2> MyIter<I1, F, I2> {
118
+ fn new(input_lines_iter: I1, process_line: F) -> Self {
119
+ MyIter {
120
+ input_lines_iter,
121
+ process_line,
122
+ processed_lines_iter: None,
123
+ next_start: 0,
124
+ }
125
+ }
126
+ }
127
+
128
+ impl<I1, F, I2> Iterator for MyIter<I1, F, I2>
129
+ where
130
+ I1: Iterator<Item = i32>, // input_linesイテレーター
131
+ F: FnMut(i32) -> I2, // process_lineクロージャー
132
+ I2: Iterator<Item = (i32, Line2)>, // process_lineの戻り値
133
+ {
134
+ type Item = (i32, Line2); // このイテレーターの戻り値型
135
+
136
+ fn next(&mut self) -> Option<Self::Item> {
137
+ loop {
138
+ // process_lineの戻り値のイテレーターがないなら、process_lineを呼び出して
139
+ // イテレーターを作る
140
+ if self.processed_lines_iter.is_none() {
141
+ let input_line = loop {
142
+ let input_line = self.input_lines_iter.next()?;
143
+ if input_line >= self.next_start {
144
+ break input_line;
145
+ }
146
+ println!("{}: skipped", input_line);
147
+ };
148
+ println!("{}: processed", input_line);
149
+
150
+ self.processed_lines_iter = Some((self.process_line)(input_line));
151
+ }
152
+
153
+ if let Some(pl_iter) = &mut self.processed_lines_iter {
154
+ // process_lineの戻り値のイテレーターから次の値を取り出す
155
+ if let Some(pl @ (end, _)) = pl_iter.next() {
156
+ self.next_start = end;
157
+ return Some(pl);
158
+ } else {
159
+ // イテレーターが終了していたら、最初からやり直す
160
+ self.processed_lines_iter = None;
161
+ continue;
162
+ }
163
+ } else {
164
+ // 全ての行を処理したので終了する
165
+ return None;
166
+ }
167
+ }
168
+ }
169
+ }
170
+
171
+ fn main() {
172
+ // process_line の出力用
173
+ let mut process_line_results = [
174
+ (0, &[(1, Line2::A), (1, Line2::B)][..]),
175
+ (1, &[]),
176
+ (2, &[(4, Line2::B)]),
177
+ // 3 はなし、4 まで飛ばすので飛ばされる
178
+ (4, &[]),
179
+ (5, &[(6, Line2::A)]),
180
+ ]
181
+ .into_iter();
182
+
183
+ // 行の内容を処理して結果を返す処理
184
+ // ここでは process_line_results から順番に返すようにしている
185
+ // ここではクロージャだが、実際は関数
186
+ // 実際も戻り値は impl Iterator
187
+ let process_line = |i| {
188
+ // 計5回呼ばれる
189
+ let (expected_i, line_items) = process_line_results.next().unwrap();
190
+ assert_eq!(i, expected_i);
191
+ line_items.into_iter().cloned() // impl Iterator<Item=(i32, Line2)>
192
+ };
193
+
194
+ // 入力データ
195
+ // ここでは単なる数値 [0,1,2,3,4,5]
196
+ // 実際も impl Iterator
197
+ let lines = 0..6_i32;
198
+
199
+ // ★メイン処理
200
+ // flattenが不要になった
201
+ let main_result = MyIter::new(lines, process_line).collect::<Vec<_>>();
202
+
203
+ // 処理結果出力
204
+ println!("main_result: {:?}", main_result);
205
+ assert_eq!(
206
+ main_result,
207
+ vec![(1, Line2::A), (1, Line2::B), (4, Line2::B), (6, Line2::A),]
208
+ );
209
+ }
210
+ ```
211
+

1

誤字を修正しました

2022/10/03 02:55

投稿

tatsuya6502
tatsuya6502

スコア2035

test CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  しかし、今回の質問のようなコードで変数を共有するには`Cell`しかなさそうですので、`Cell`を使うのは適当だと思います。
6
6
 
7
- 余談ですが、ご質問コードで試したところ、片方のクロージャーを構造体で置き換え、その構造体に`Iterator`を実装することで、`Cell`を使わずに同じ機能を実現できました。簡略化前のコードに適用できるかはわかりませんが、参考になるかもしれません。
7
+ 余談ですが、ご質問コードで試したところ、片方のクロージャーを構造体で置き換え、その構造体に`Iterator`を実装することで、`Cell`を使わずに同じ機能を実現できました。簡略化前のコードに適用できるかはわかりませんが、参考になるかもしれません。
8
8
 
9
9
  ```rust
10
10
  #[derive(Debug, Clone, PartialEq, Eq)]