Skip to content

Commit e4a484d

Browse files
committed
Fix helpers unable to use borrowed arguments
This is an extension of acabcf6, which fixed this problem, but only for one of the helpers. Now, it should be fixed for all of them.
1 parent cfde3c3 commit e4a484d

File tree

1 file changed

+171
-16
lines changed

1 file changed

+171
-16
lines changed

binrw/src/helpers.rs

+171-16
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use core::iter::from_fn;
1616
///
1717
/// # Examples
1818
///
19+
/// Reading null-terminated data:
20+
///
1921
/// ```
2022
/// # use binrw::{BinRead, helpers::until, io::Cursor, BinReaderExt};
2123
/// #[derive(BinRead)]
@@ -28,17 +30,53 @@ use core::iter::from_fn;
2830
/// # let x: NullTerminated = x.read_be().unwrap();
2931
/// # assert_eq!(x.data, &[1, 2, 3, 4, 0]);
3032
/// ```
31-
pub fn until<Ret, T, Arg, CondFn, Reader>(
33+
///
34+
/// Reading byte-terminated data with a header that is used to read entries:
35+
///
36+
/// ```
37+
/// # // This test is checking to make sure that borrowed arguments work.
38+
/// #
39+
/// # use binrw::{BinRead, helpers::until, io::Cursor, BinReaderExt};
40+
/// #[derive(BinRead)]
41+
/// struct Header {
42+
/// terminator: u8,
43+
/// extra: u8,
44+
/// };
45+
///
46+
/// #[derive(BinRead)]
47+
/// # #[derive(Debug, Eq, PartialEq)]
48+
/// #[br(import(header: &Header))]
49+
/// struct Entry(
50+
/// #[br(map = |value: u8| value + header.extra)]
51+
/// u8
52+
/// );
53+
///
54+
/// #[derive(BinRead)]
55+
/// struct ByteTerminated {
56+
/// header: Header,
57+
///
58+
/// #[br(
59+
/// parse_with = until(|entry: &Entry| entry.0 == header.terminator),
60+
/// args(&header)
61+
/// )]
62+
/// data: Vec<Entry>,
63+
/// }
64+
///
65+
/// # let mut x = Cursor::new(b"\xff\x01\x02\x03\x04\xfe");
66+
/// # let x: ByteTerminated = x.read_be().unwrap();
67+
/// # assert_eq!(x.data, &[Entry(3), Entry(4), Entry(5), Entry(255)]);
68+
/// ```
69+
pub fn until<'a, Ret, T, Arg, CondFn, Reader>(
3270
cond: CondFn,
3371
) -> impl Fn(&mut Reader, Endian, Arg) -> BinResult<Ret>
3472
where
3573
Ret: FromIterator<T>,
36-
T: for<'a> BinRead<Args<'a> = Arg>,
74+
T: BinRead<Args<'a> = Arg>,
3775
Arg: Clone,
3876
CondFn: Fn(&T) -> bool,
3977
Reader: Read + Seek,
4078
{
41-
until_with(cond, T::read_options)
79+
use_with!(until_with, T, cond)
4280
}
4381

4482
/// Creates a parser that uses a given function to read items into a collection
@@ -108,6 +146,8 @@ where
108146
///
109147
/// # Examples
110148
///
149+
/// Reading null-terminated data:
150+
///
111151
/// ```
112152
/// # use binrw::{BinRead, helpers::until_exclusive, io::Cursor, BinReaderExt};
113153
/// #[derive(BinRead)]
@@ -120,17 +160,52 @@ where
120160
/// # let x: NullTerminated = x.read_be().unwrap();
121161
/// # assert_eq!(x.data, &[1, 2, 3, 4]);
122162
/// ```
123-
pub fn until_exclusive<Ret, T, Arg, CondFn, Reader>(
163+
///
164+
/// Reading byte-terminated data with a header that is required to read entries:
165+
///
166+
/// ```
167+
/// # // This test is checking to make sure that borrowed arguments work.
168+
/// #
169+
/// # use binrw::{BinRead, helpers::until_exclusive, io::Cursor, BinReaderExt};
170+
/// #[derive(BinRead)]
171+
/// struct Header {
172+
/// terminator: u8,
173+
/// extra: u8,
174+
/// };
175+
///
176+
/// #[derive(BinRead)]
177+
/// # #[derive(Debug, Eq, PartialEq)]
178+
/// #[br(import(header: &Header))]
179+
/// struct Entry(
180+
/// #[br(map = |value: u8| value + header.extra)]
181+
/// u8
182+
/// );
183+
///
184+
/// #[derive(BinRead)]
185+
/// struct ByteTerminated {
186+
/// header: Header,
187+
///
188+
/// #[br(parse_with = until_exclusive(|entry: &Entry| {
189+
/// entry.0 == header.terminator
190+
/// }), args(&header))]
191+
/// data: Vec<Entry>,
192+
/// }
193+
///
194+
/// # let mut x = Cursor::new(b"\xff\x01\x02\x03\x04\xfe");
195+
/// # let x: ByteTerminated = x.read_be().unwrap();
196+
/// # assert_eq!(x.data, &[Entry(3), Entry(4), Entry(5)]);
197+
/// ```
198+
pub fn until_exclusive<'a, Ret, T, Arg, CondFn, Reader>(
124199
cond: CondFn,
125200
) -> impl Fn(&mut Reader, Endian, Arg) -> BinResult<Ret>
126201
where
127202
Ret: FromIterator<T>,
128-
T: for<'a> BinRead<Args<'a> = Arg>,
203+
T: BinRead<Args<'a> = Arg>,
129204
Arg: Clone,
130205
CondFn: Fn(&T) -> bool,
131206
Reader: Read + Seek,
132207
{
133-
until_exclusive_with(cond, T::read_options)
208+
use_with!(until_exclusive_with, T, cond)
134209
}
135210

136211
/// Creates a parser that uses a given function to read items into a collection
@@ -199,6 +274,8 @@ where
199274
///
200275
/// # Examples
201276
///
277+
/// Reading an entire file at once:
278+
///
202279
/// ```
203280
/// # use binrw::{BinRead, helpers::until_eof, io::Cursor, BinReaderExt};
204281
/// #[derive(BinRead)]
@@ -211,18 +288,50 @@ where
211288
/// # let x: EntireFile = x.read_be().unwrap();
212289
/// # assert_eq!(x.data, &[1, 2, 3, 4]);
213290
/// ```
214-
pub fn until_eof<Ret, T, Arg, Reader>(
291+
///
292+
/// Reading an entire file with a header that is used to read entries:
293+
///
294+
/// ```
295+
/// # // This test is checking to make sure that borrowed arguments work.
296+
/// #
297+
/// # use binrw::{BinRead, helpers::until_eof, io::Cursor, BinReaderExt};
298+
/// #[derive(BinRead)]
299+
/// struct Header {
300+
/// extra: u8,
301+
/// };
302+
///
303+
/// #[derive(BinRead)]
304+
/// # #[derive(Debug, Eq, PartialEq)]
305+
/// #[br(import(header: &Header))]
306+
/// struct Entry(
307+
/// #[br(map = |value: u8| value + header.extra)]
308+
/// u8
309+
/// );
310+
///
311+
/// #[derive(BinRead)]
312+
/// struct EntireFile {
313+
/// header: Header,
314+
///
315+
/// #[br(parse_with = until_eof, args(&header))]
316+
/// data: Vec<Entry>,
317+
/// }
318+
///
319+
/// # let mut x = Cursor::new(b"\x01\x02\x03\x04");
320+
/// # let x: EntireFile = x.read_be().unwrap();
321+
/// # assert_eq!(x.data, &[Entry(3), Entry(4), Entry(5)]);
322+
/// ```
323+
pub fn until_eof<'a, Ret, T, Arg, Reader>(
215324
reader: &mut Reader,
216325
endian: Endian,
217326
args: Arg,
218327
) -> BinResult<Ret>
219328
where
220329
Ret: FromIterator<T>,
221-
T: for<'a> BinRead<Args<'a> = Arg>,
330+
T: BinRead<Args<'a> = Arg>,
222331
Arg: Clone,
223332
Reader: Read + Seek,
224333
{
225-
until_eof_with(T::read_options)(reader, endian, args)
334+
use_with!(until_eof_with, T)(reader, endian, args)
226335
}
227336

228337
/// Creates a parser that uses a given function to read items into a collection
@@ -359,13 +468,7 @@ where
359468
It: IntoIterator<Item = Arg>,
360469
Reader: Read + Seek,
361470
{
362-
// For an unknown reason (possibly related to the note in the compiler error
363-
// that says “due to current limitations in the borrow checker”), trying to
364-
// pass `T::read_options` directly does not work, but passing a closure like
365-
// this works just fine
366-
args_iter_with(it, |reader, options, arg| {
367-
T::read_options(reader, options, arg)
368-
})
471+
use_with!(args_iter_with, T, it)
369472
}
370473

371474
/// Creates a parser that uses a given function to build a collection, using
@@ -430,6 +533,8 @@ where
430533
///
431534
/// # Examples
432535
///
536+
/// Reading data of a fixed size:
537+
///
433538
/// ```
434539
/// # use binrw::{BinRead, helpers::count, io::Cursor, BinReaderExt};
435540
/// # use std::collections::VecDeque;
@@ -445,6 +550,40 @@ where
445550
/// # let x: CountBytes = x.read_be().unwrap();
446551
/// # assert_eq!(x.data, &[1, 2, 3]);
447552
/// ```
553+
///
554+
/// Reading fixed-size data with a header that is used to read entries:
555+
///
556+
/// ```
557+
/// # // This test is checking to make sure that borrowed arguments work.
558+
/// #
559+
/// # use binrw::{BinRead, helpers::count, io::Cursor, BinReaderExt};
560+
/// # use std::collections::VecDeque;
561+
/// #[derive(BinRead)]
562+
/// struct Header {
563+
/// len: u8,
564+
/// extra: u8,
565+
/// };
566+
///
567+
/// #[derive(BinRead)]
568+
/// # #[derive(Debug, Eq, PartialEq)]
569+
/// #[br(import(header: &Header))]
570+
/// struct Entry(
571+
/// #[br(map = |value: u8| value + header.extra)]
572+
/// u8
573+
/// );
574+
///
575+
/// #[derive(BinRead)]
576+
/// struct CountBytes {
577+
/// header: Header,
578+
///
579+
/// #[br(parse_with = count(header.len.into()), args(&header))]
580+
/// data: VecDeque<Entry>,
581+
/// }
582+
///
583+
/// # let mut x = Cursor::new(b"\x02\x01\x02\x03");
584+
/// # let x: CountBytes = x.read_be().unwrap();
585+
/// # assert_eq!(x.data, &[Entry(3), Entry(4)]);
586+
/// ```
448587
pub fn count<'a, Ret, T, Arg, Reader>(
449588
n: usize,
450589
) -> impl Fn(&mut Reader, Endian, Arg) -> BinResult<Ret>
@@ -615,6 +754,22 @@ fn not_enough_bytes<T>(_: T) -> Error {
615754
))
616755
}
617756

757+
// For an unknown reason (possibly related to the note in the compiler error
758+
// that says “due to current limitations in the borrow checker”), passing
759+
// `T::read_options` directly to any of the `with` helper functions does
760+
// not work (“requires that `'a` must outlive `'static`” and “one type is more
761+
// general than the other”), but passing a closure that calls `T::read_options`
762+
// itself works fine
763+
macro_rules! use_with {
764+
($fn:ident, $Ty:ty $(, $args:tt)* $(,)?) => {
765+
$fn($($args,)* |reader, options, args| {
766+
<$Ty>::read_options(reader, options, args)
767+
})
768+
}
769+
}
770+
771+
use use_with;
772+
618773
macro_rules! vec_fast_int {
619774
(try ($($Ty:ty)+) using ($list:expr, $reader:expr, $endian:expr, $count:expr) else { $($else:tt)* }) => {
620775
$(if let Some(list) = <dyn core::any::Any>::downcast_mut::<Vec<$Ty>>(&mut $list) {

0 commit comments

Comments
 (0)