consensus_encoding: remove implicit lifetime name
What changed, and why it matters
This commit is a routine code cleanup in the Rust Bitcoin library. It removes an unnecessary naming requirement for lifetimes in the encoding machinery, making the macro more flexible and the code easier to maintain. There is no security issue here.
No security action required. This is a benign refactoring commit.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors the Encoder trait from Encoder<'e> to Encoder by eliding the lifetime parameter. This removes the implicit requirement in the encoder_newtype! macro that the struct lifetime be named 'e. It updates all impl blocks and adds a test verifying that the macro works with custom lifetime names and with types that have no lifetime. This is a compile-time ergonomics/API improvement, not a behavioral change.
Changed components
consensus_encoding/src/encode/encoders.rsconsensus_encoding/src/encode/mod.rsconsensus_encoding/tests/encode.rsInspect captured patch +33 / −23
diff --git a/consensus_encoding/src/encode/encoders.rs b/consensus_encoding/src/encode/encoders.rs
index 3f4931f7..666e3cdc 100644
--- a/consensus_encoding/src/encode/encoders.rs
+++ b/consensus_encoding/src/encode/encoders.rs
@@ -37,7 +37,7 @@ impl<'sl> BytesEncoder<'sl> {
}
}
-impl Encoder<'_> for BytesEncoder<'_> {
+impl Encoder for BytesEncoder<'_> {
fn current_chunk(&self) -> Option<&[u8]> {
if let Some(compact_size) = self.compact_size.as_ref() {
Some(compact_size)
@@ -67,7 +67,7 @@ impl<const N: usize> ArrayEncoder<N> {
pub fn without_length_prefix(arr: [u8; N]) -> Self { Self { arr: Some(arr) } }
}
-impl<const N: usize> Encoder<'_> for ArrayEncoder<N> {
+impl<const N: usize> Encoder for ArrayEncoder<N> {
fn current_chunk(&self) -> Option<&[u8]> { self.arr.as_ref().map(|x| &x[..]) }
fn advance(&mut self) -> bool {
@@ -104,7 +104,7 @@ impl<'e, T: Encodable> SliceEncoder<'e, T> {
}
}
-impl<'e, T: Encodable> Encoder<'e> for SliceEncoder<'e, T> {
+impl<'e, T: Encodable> Encoder for SliceEncoder<'e, T> {
fn current_chunk(&self) -> Option<&[u8]> {
if let Some(compact_size) = self.compact_size.as_ref() {
return Some(compact_size);
@@ -160,7 +160,7 @@ impl<A, B> Encoder2<A, B> {
pub fn new(enc_1: A, enc_2: B) -> Self { Self { enc_idx: 0, enc_1, enc_2 } }
}
-impl<'e, A: Encoder<'e>, B: Encoder<'e>> Encoder<'e> for Encoder2<A, B> {
+impl<A: Encoder, B: Encoder> Encoder for Encoder2<A, B> {
fn current_chunk(&self) -> Option<&[u8]> {
if self.enc_idx == 0 {
self.enc_1.current_chunk()
@@ -198,7 +198,7 @@ impl<A, B, C> Encoder3<A, B, C> {
}
}
-impl<'e, A: Encoder<'e>, B: Encoder<'e>, C: Encoder<'e>> Encoder<'e> for Encoder3<A, B, C> {
+impl<A: Encoder, B: Encoder, C: Encoder> Encoder for Encoder3<A, B, C> {
fn current_chunk(&self) -> Option<&[u8]> { self.inner.current_chunk() }
fn advance(&mut self) -> bool { self.inner.advance() }
}
@@ -215,9 +215,7 @@ impl<A, B, C, D> Encoder4<A, B, C, D> {
}
}
-impl<'e, A: Encoder<'e>, B: Encoder<'e>, C: Encoder<'e>, D: Encoder<'e>> Encoder<'e>
- for Encoder4<A, B, C, D>
-{
+impl<A: Encoder, B: Encoder, C: Encoder, D: Encoder> Encoder for Encoder4<A, B, C, D> {
fn current_chunk(&self) -> Option<&[u8]> { self.inner.current_chunk() }
fn advance(&mut self) -> bool { self.inner.advance() }
}
@@ -239,15 +237,8 @@ impl<A, B, C, D, E, F> Encoder6<A, B, C, D, E, F> {
}
}
-impl<
- 'e,
- A: Encoder<'e>,
- B: Encoder<'e>,
- C: Encoder<'e>,
- D: Encoder<'e>,
- E: Encoder<'e>,
- F: Encoder<'e>,
- > Encoder<'e> for Encoder6<A, B, C, D, E, F>
+impl<A: Encoder, B: Encoder, C: Encoder, D: Encoder, E: Encoder, F: Encoder> Encoder
+ for Encoder6<A, B, C, D, E, F>
{
fn current_chunk(&self) -> Option<&[u8]> { self.inner.current_chunk() }
fn advance(&mut self) -> bool { self.inner.advance() }
@@ -261,7 +252,7 @@ mod tests {
use super::*;
// Run the encoder i.e., use it to encode into a vector.
- fn run_encoder<'e>(mut encoder: impl Encoder<'e>) -> Vec<u8> {
+ fn run_encoder(mut encoder: impl Encoder) -> Vec<u8> {
let mut vec = Vec::new();
while let Some(chunk) = encoder.current_chunk() {
vec.extend_from_slice(chunk);
@@ -311,4 +302,4 @@ mod tests {
let want = [0u8];
assert_eq!(got, want);
}
- }
+}
diff --git a/consensus_encoding/src/encode/mod.rs b/consensus_encoding/src/encode/mod.rs
index 223006af..4eb43444 100644
--- a/consensus_encoding/src/encode/mod.rs
+++ b/consensus_encoding/src/encode/mod.rs
@@ -15,7 +15,7 @@ pub mod encoders;
pub trait Encodable {
/// The encoder associated with this type. Conceptually, the encoder is like
/// an iterator which yields byte slices.
- type Encoder<'s>: Encoder<'s>
+ type Encoder<'s>: Encoder
where
Self: 's;
@@ -24,7 +24,7 @@ pub trait Encodable {
}
/// An encoder for a consensus-encodable object.
-pub trait Encoder<'e> {
+pub trait Encoder {
/// Yields the current encoded byteslice.
///
/// Will always return the same value until [`Self::advance`] is called.
@@ -55,7 +55,7 @@ macro_rules! encoder_newtype{
$(#[$($struct_attr)*])*
pub struct $name$(<$lt>)?($encoder);
- impl<'e> $crate::Encoder<'e> for $name$(<$lt>)? {
+ impl$(<$lt>)? $crate::Encoder for $name$(<$lt>)? {
#[inline]
fn current_chunk(&self) -> Option<&[u8]> { self.0.current_chunk() }
diff --git a/consensus_encoding/tests/encode.rs b/consensus_encoding/tests/encode.rs
index e8531155..8865b4a4 100644
--- a/consensus_encoding/tests/encode.rs
+++ b/consensus_encoding/tests/encode.rs
@@ -5,7 +5,7 @@
#[cfg(feature = "std")]
use std::io::{Cursor, Write};
-use consensus_encoding::{ArrayEncoder, Encodable};
+use consensus_encoding::{ArrayEncoder, BytesEncoder, Encodable, Encoder};
// Simple test type that implements Encodable.
struct TestData(u32);
@@ -94,3 +94,22 @@ fn encode_std_writer_io_error() {
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), std::io::ErrorKind::Other);
}
+
+#[test]
+fn encode_newtype_lifetime_flexibility() {
+ // Test that the encoder_newtype macro allows different lifetime names.
+
+ consensus_encoding::encoder_newtype! {
+ pub struct CustomEncoder<'data>(BytesEncoder<'data>);
+ }
+ consensus_encoding::encoder_newtype! {
+ pub struct NoLifetimeEncoder(ArrayEncoder<4>);
+ }
+
+ let test_data = b"hello world";
+ let custom_encoder = CustomEncoder(BytesEncoder::without_length_prefix(test_data));
+ let no_lifetime_encoder = NoLifetimeEncoder(ArrayEncoder::without_length_prefix([1, 2, 3, 4]));
+
+ assert_eq!(custom_encoder.current_chunk(), Some(test_data.as_slice()));
+ assert_eq!(no_lifetime_encoder.current_chunk(), Some(&[1, 2, 3, 4][..]));
+}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.