Skip to content

Commit

Permalink
Make Read/BufRead traits dyn compatible
Browse files Browse the repository at this point in the history
Currently our `Read` and `BufRead` traits are not dyn compatible (object
safe) because of `Take` having an unsized trait bound. We just removed
the requirement for this by removing the bound from `consensus_encode`.

Remove the `?Sized` trait bound from `Take` and add a test that shows
that `Read` and `BufRead` are now dyn compatible.

Fix: rust-bitcoin#3833
  • Loading branch information
tcharding committed Jan 17, 2025
1 parent 789c578 commit d435574
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 6 deletions.
20 changes: 14 additions & 6 deletions io/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ pub trait Read {

/// Constructs a new adapter which will read at most `limit` bytes.
#[inline]
fn take(&mut self, limit: u64) -> Take<Self> { Take { reader: self, remaining: limit } }
fn take(&mut self, limit: u64) -> Take<Self>
where
Self: Sized,
{
Take { reader: self, remaining: limit }
}

/// Attempts to read up to limit bytes from the reader, allocating space in `buf` as needed.
///
Expand All @@ -73,7 +78,10 @@ pub trait Read {
#[doc(alias = "read_to_end")]
#[cfg(feature = "alloc")]
#[inline]
fn read_to_limit(&mut self, buf: &mut Vec<u8>, limit: u64) -> Result<usize> {
fn read_to_limit(&mut self, buf: &mut Vec<u8>, limit: u64) -> Result<usize>
where
Self: Sized,
{
self.take(limit).read_to_end(buf)
}
}
Expand All @@ -95,12 +103,12 @@ pub trait BufRead: Read {
///
/// Created by calling `[Read::take]`.
#[derive(Debug)]
pub struct Take<'a, R: Read + ?Sized> {
pub struct Take<'a, R: Read> {
reader: &'a mut R,
remaining: u64,
}

impl<R: Read + ?Sized> Take<'_, R> {
impl<R: Read> Take<'_, R> {
/// Reads all bytes until EOF from the underlying reader into `buf`.
#[cfg(feature = "alloc")]
#[inline]
Expand All @@ -122,7 +130,7 @@ impl<R: Read + ?Sized> Take<'_, R> {
}
}

impl<R: Read + ?Sized> Read for Take<'_, R> {
impl<R: Read> Read for Take<'_, R> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let len = cmp::min(buf.len(), self.remaining.try_into().unwrap_or(buf.len()));
Expand All @@ -133,7 +141,7 @@ impl<R: Read + ?Sized> Read for Take<'_, R> {
}

// Impl copied from Rust stdlib.
impl<R: BufRead + ?Sized> BufRead for Take<'_, R> {
impl<R: BufRead> BufRead for Take<'_, R> {
#[inline]
fn fill_buf(&mut self) -> Result<&[u8]> {
// Don't call into inner reader at all at EOF because it may still block
Expand Down
16 changes: 16 additions & 0 deletions io/tests/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,19 @@ fn all_non_error_tyes_implement_send_sync() {
assert_send::<Types>();
assert_sync::<Types>();
}

#[test]
fn object_safety() {
// Sanity check, these are all object safe.
struct StdlibTraits {
p: Box<dyn std::io::Read>,
q: Box<dyn std::io::Write>,
r: Box<dyn std::io::BufRead>,
}
// If this builds then our three traits are object safe also.
struct OurTraits {
p: Box<dyn Read>,
q: Box<dyn Write>,
r: Box<dyn BufRead>,
}
}

0 comments on commit d435574

Please sign in to comment.