mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
get rid of default implementation of Hashed trait for writable things (#2573)
This commit is contained in:
parent
d4540f32a3
commit
99494c6fa6
12 changed files with 74 additions and 34 deletions
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
//! Lightweight readonly view into output MMR for convenience.
|
//! Lightweight readonly view into output MMR for convenience.
|
||||||
|
|
||||||
use crate::core::core::hash::Hash;
|
use crate::core::core::hash::{Hash, Hashed};
|
||||||
use crate::core::core::pmmr::{self, ReadonlyPMMR};
|
use crate::core::core::pmmr::{self, ReadonlyPMMR};
|
||||||
use crate::core::core::{Block, BlockHeader, Input, Output, Transaction};
|
use crate::core::core::{Block, BlockHeader, Input, Output, Transaction};
|
||||||
use crate::core::global;
|
use crate::core::global;
|
||||||
|
|
|
@ -73,10 +73,11 @@ impl Tip {
|
||||||
total_difficulty: header.total_difficulty(),
|
total_difficulty: header.total_difficulty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// *Really* easy to accidentally call hash() on a tip (thinking its a header).
|
impl Hashed for Tip {
|
||||||
/// So lets make hash() do the right thing here.
|
/// The hash of the underlying block.
|
||||||
pub fn hash(&self) -> Hash {
|
fn hash(&self) -> Hash {
|
||||||
self.last_block_h
|
self.last_block_h
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ use std::sync::Arc;
|
||||||
use crate::consensus::{reward, REWARD};
|
use crate::consensus::{reward, REWARD};
|
||||||
use crate::core::committed::{self, Committed};
|
use crate::core::committed::{self, Committed};
|
||||||
use crate::core::compact_block::{CompactBlock, CompactBlockBody};
|
use crate::core::compact_block::{CompactBlock, CompactBlockBody};
|
||||||
use crate::core::hash::{Hash, Hashed, ZERO_HASH};
|
use crate::core::hash::{DefaultHashable, Hash, Hashed, ZERO_HASH};
|
||||||
use crate::core::verifier_cache::VerifierCache;
|
use crate::core::verifier_cache::VerifierCache;
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
transaction, Commitment, Input, Output, Transaction, TransactionBody, TxKernel, Weighting,
|
transaction, Commitment, Input, Output, Transaction, TransactionBody, TxKernel, Weighting,
|
||||||
|
@ -160,9 +160,9 @@ impl FixedLength for HeaderEntry {
|
||||||
const LEN: usize = Hash::LEN + 8 + Difficulty::LEN + 4 + 1;
|
const LEN: usize = Hash::LEN + 8 + Difficulty::LEN + 4 + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeaderEntry {
|
impl Hashed for HeaderEntry {
|
||||||
/// The hash of the underlying block.
|
/// The hash of the underlying block.
|
||||||
pub fn hash(&self) -> Hash {
|
fn hash(&self) -> Hash {
|
||||||
self.hash
|
self.hash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,6 +197,7 @@ pub struct BlockHeader {
|
||||||
/// Proof of work and related
|
/// Proof of work and related
|
||||||
pub pow: ProofOfWork,
|
pub pow: ProofOfWork,
|
||||||
}
|
}
|
||||||
|
impl DefaultHashable for BlockHeader {}
|
||||||
|
|
||||||
impl Default for BlockHeader {
|
impl Default for BlockHeader {
|
||||||
fn default() -> BlockHeader {
|
fn default() -> BlockHeader {
|
||||||
|
@ -353,6 +354,13 @@ pub struct Block {
|
||||||
body: TransactionBody,
|
body: TransactionBody,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Hashed for Block {
|
||||||
|
/// The hash of the underlying block.
|
||||||
|
fn hash(&self) -> Hash {
|
||||||
|
self.header.hash()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Implementation of Writeable for a block, defines how to write the block to a
|
/// Implementation of Writeable for a block, defines how to write the block to a
|
||||||
/// binary writer. Differentiates between writing the block for the purpose of
|
/// binary writer. Differentiates between writing the block for the purpose of
|
||||||
/// full serialization and the one of just extracting a hash.
|
/// full serialization and the one of just extracting a hash.
|
||||||
|
@ -570,11 +578,6 @@ impl Block {
|
||||||
&mut self.body.kernels
|
&mut self.body.kernels
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blockhash, computed using only the POW
|
|
||||||
pub fn hash(&self) -> Hash {
|
|
||||||
self.header.hash()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sum of all fees (inputs less outputs) in the block
|
/// Sum of all fees (inputs less outputs) in the block
|
||||||
pub fn total_fees(&self) -> u64 {
|
pub fn total_fees(&self) -> u64 {
|
||||||
self.body
|
self.body
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
|
|
||||||
use crate::core::block::{Block, BlockHeader, Error};
|
use crate::core::block::{Block, BlockHeader, Error};
|
||||||
use crate::core::hash::Hashed;
|
use crate::core::hash::{DefaultHashable, Hashed};
|
||||||
use crate::core::id::ShortIdentifiable;
|
use crate::core::id::ShortIdentifiable;
|
||||||
use crate::core::{Output, ShortId, TxKernel};
|
use crate::core::{Output, ShortId, TxKernel};
|
||||||
use crate::ser::{self, read_multi, Readable, Reader, VerifySortedAndUnique, Writeable, Writer};
|
use crate::ser::{self, read_multi, Readable, Reader, VerifySortedAndUnique, Writeable, Writer};
|
||||||
|
@ -137,6 +137,8 @@ pub struct CompactBlock {
|
||||||
body: CompactBlockBody,
|
body: CompactBlockBody,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DefaultHashable for CompactBlock {}
|
||||||
|
|
||||||
impl CompactBlock {
|
impl CompactBlock {
|
||||||
/// "Lightweight" validation.
|
/// "Lightweight" validation.
|
||||||
fn validate_read(&self) -> Result<(), Error> {
|
fn validate_read(&self) -> Result<(), Error> {
|
||||||
|
|
|
@ -36,6 +36,19 @@ pub const ZERO_HASH: Hash = Hash([0; 32]);
|
||||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||||
pub struct Hash([u8; 32]);
|
pub struct Hash([u8; 32]);
|
||||||
|
|
||||||
|
impl DefaultHashable for Hash {}
|
||||||
|
|
||||||
|
impl Hash {
|
||||||
|
fn hash_with<T: Writeable>(&self, other: T) -> Hash {
|
||||||
|
let mut hasher = HashWriter::default();
|
||||||
|
ser::Writeable::write(self, &mut hasher).unwrap();
|
||||||
|
ser::Writeable::write(&other, &mut hasher).unwrap();
|
||||||
|
let mut ret = [0; 32];
|
||||||
|
hasher.finalize(&mut ret);
|
||||||
|
Hash(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Hash {
|
impl fmt::Debug for Hash {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let hash_hex = self.to_hex();
|
let hash_hex = self.to_hex();
|
||||||
|
@ -212,25 +225,26 @@ impl ser::Writer for HashWriter {
|
||||||
pub trait Hashed {
|
pub trait Hashed {
|
||||||
/// Obtain the hash of the object
|
/// Obtain the hash of the object
|
||||||
fn hash(&self) -> Hash;
|
fn hash(&self) -> Hash;
|
||||||
/// Hash the object together with another writeable object
|
|
||||||
fn hash_with<T: Writeable>(&self, other: T) -> Hash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: ser::Writeable> Hashed for W {
|
/// Implementing this trait enables the default
|
||||||
|
/// hash implementation
|
||||||
|
pub trait DefaultHashable: Writeable {}
|
||||||
|
impl<D: DefaultHashable> Hashed for D {
|
||||||
fn hash(&self) -> Hash {
|
fn hash(&self) -> Hash {
|
||||||
let mut hasher = HashWriter::default();
|
let mut hasher = HashWriter::default();
|
||||||
ser::Writeable::write(self, &mut hasher).unwrap();
|
Writeable::write(self, &mut hasher).unwrap();
|
||||||
let mut ret = [0; 32];
|
|
||||||
hasher.finalize(&mut ret);
|
|
||||||
Hash(ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hash_with<T: Writeable>(&self, other: T) -> Hash {
|
|
||||||
let mut hasher = HashWriter::default();
|
|
||||||
ser::Writeable::write(self, &mut hasher).unwrap();
|
|
||||||
ser::Writeable::write(&other, &mut hasher).unwrap();
|
|
||||||
let mut ret = [0; 32];
|
let mut ret = [0; 32];
|
||||||
hasher.finalize(&mut ret);
|
hasher.finalize(&mut ret);
|
||||||
Hash(ret)
|
Hash(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<D: DefaultHashable> DefaultHashable for &D {}
|
||||||
|
impl<D: DefaultHashable, E: DefaultHashable> DefaultHashable for (D, E) {}
|
||||||
|
impl<D: DefaultHashable, E: DefaultHashable, F: DefaultHashable> DefaultHashable for (D, E, F) {}
|
||||||
|
|
||||||
|
/// Implement Hashed trait for external types here
|
||||||
|
impl DefaultHashable for crate::util::secp::pedersen::RangeProof {}
|
||||||
|
impl DefaultHashable for Vec<u8> {}
|
||||||
|
impl DefaultHashable for u64 {}
|
||||||
|
|
|
@ -20,7 +20,7 @@ use std::cmp::Ordering;
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
use siphasher::sip::SipHasher24;
|
use siphasher::sip::SipHasher24;
|
||||||
|
|
||||||
use crate::core::hash::{Hash, Hashed};
|
use crate::core::hash::{DefaultHashable, Hash, Hashed};
|
||||||
use crate::ser::{self, Readable, Reader, Writeable, Writer};
|
use crate::ser::{self, Readable, Reader, Writeable, Writer};
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
|
@ -73,6 +73,7 @@ impl<H: Hashed> ShortIdentifiable for H {
|
||||||
#[derive(Clone, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Serialize, Deserialize, Hash)]
|
||||||
pub struct ShortId([u8; 6]);
|
pub struct ShortId([u8; 6]);
|
||||||
|
|
||||||
|
impl DefaultHashable for ShortId {}
|
||||||
/// We want to sort short_ids in a canonical and consistent manner so we can
|
/// We want to sort short_ids in a canonical and consistent manner so we can
|
||||||
/// verify sort order in the same way we do for full inputs|outputs|kernels
|
/// verify sort order in the same way we do for full inputs|outputs|kernels
|
||||||
/// themselves.
|
/// themselves.
|
||||||
|
@ -168,6 +169,8 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DefaultHashable for Foo {}
|
||||||
|
|
||||||
let foo = Foo(0);
|
let foo = Foo(0);
|
||||||
|
|
||||||
let expected_hash =
|
let expected_hash =
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
//! Transactions
|
//! Transactions
|
||||||
|
|
||||||
use crate::core::hash::Hashed;
|
use crate::core::hash::{DefaultHashable, Hashed};
|
||||||
use crate::core::verifier_cache::VerifierCache;
|
use crate::core::verifier_cache::VerifierCache;
|
||||||
use crate::core::{committed, Committed};
|
use crate::core::{committed, Committed};
|
||||||
use crate::keychain::{self, BlindingFactor};
|
use crate::keychain::{self, BlindingFactor};
|
||||||
|
@ -50,6 +50,8 @@ enum_from_primitive! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DefaultHashable for KernelFeatures {}
|
||||||
|
|
||||||
impl Writeable for KernelFeatures {
|
impl Writeable for KernelFeatures {
|
||||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
|
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
|
||||||
writer.write_u8(*self as u8)?;
|
writer.write_u8(*self as u8)?;
|
||||||
|
@ -170,6 +172,7 @@ pub struct TxKernel {
|
||||||
pub excess_sig: secp::Signature,
|
pub excess_sig: secp::Signature,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DefaultHashable for TxKernel {}
|
||||||
hashable_ord!(TxKernel);
|
hashable_ord!(TxKernel);
|
||||||
|
|
||||||
impl ::std::hash::Hash for TxKernel {
|
impl ::std::hash::Hash for TxKernel {
|
||||||
|
@ -753,6 +756,8 @@ pub struct Transaction {
|
||||||
body: TransactionBody,
|
body: TransactionBody,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DefaultHashable for Transaction {}
|
||||||
|
|
||||||
/// PartialEq
|
/// PartialEq
|
||||||
impl PartialEq for Transaction {
|
impl PartialEq for Transaction {
|
||||||
fn eq(&self, tx: &Transaction) -> bool {
|
fn eq(&self, tx: &Transaction) -> bool {
|
||||||
|
@ -1113,6 +1118,7 @@ pub struct Input {
|
||||||
pub commit: Commitment,
|
pub commit: Commitment,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DefaultHashable for Input {}
|
||||||
hashable_ord!(Input);
|
hashable_ord!(Input);
|
||||||
|
|
||||||
impl ::std::hash::Hash for Input {
|
impl ::std::hash::Hash for Input {
|
||||||
|
@ -1218,6 +1224,7 @@ pub struct Output {
|
||||||
pub proof: RangeProof,
|
pub proof: RangeProof,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DefaultHashable for Output {}
|
||||||
hashable_ord!(Output);
|
hashable_ord!(Output);
|
||||||
|
|
||||||
impl ::std::hash::Hash for Output {
|
impl ::std::hash::Hash for Output {
|
||||||
|
@ -1330,6 +1337,8 @@ pub struct OutputIdentifier {
|
||||||
pub commit: Commitment,
|
pub commit: Commitment,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DefaultHashable for OutputIdentifier {}
|
||||||
|
|
||||||
impl OutputIdentifier {
|
impl OutputIdentifier {
|
||||||
/// Build a new output_identifier.
|
/// Build a new output_identifier.
|
||||||
pub fn new(features: OutputFeatures, commit: &Commitment) -> OutputIdentifier {
|
pub fn new(features: OutputFeatures, commit: &Commitment) -> OutputIdentifier {
|
||||||
|
|
|
@ -22,7 +22,7 @@ use rand::{thread_rng, Rng};
|
||||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
use crate::consensus::{graph_weight, MIN_DIFFICULTY, SECOND_POW_EDGE_BITS};
|
use crate::consensus::{graph_weight, MIN_DIFFICULTY, SECOND_POW_EDGE_BITS};
|
||||||
use crate::core::hash::Hashed;
|
use crate::core::hash::{DefaultHashable, Hashed};
|
||||||
use crate::global;
|
use crate::global;
|
||||||
use crate::ser::{self, FixedLength, Readable, Reader, Writeable, Writer};
|
use crate::ser::{self, FixedLength, Readable, Reader, Writeable, Writer};
|
||||||
|
|
||||||
|
@ -324,6 +324,8 @@ pub struct Proof {
|
||||||
pub nonces: Vec<u64>,
|
pub nonces: Vec<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DefaultHashable for Proof {}
|
||||||
|
|
||||||
impl fmt::Debug for Proof {
|
impl fmt::Debug for Proof {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "Cuckoo{}(", self.edge_bits)?;
|
write!(f, "Cuckoo{}(", self.edge_bits)?;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
//! To use it simply implement `Writeable` or `Readable` and then use the
|
//! To use it simply implement `Writeable` or `Readable` and then use the
|
||||||
//! `serialize` or `deserialize` functions on them as appropriate.
|
//! `serialize` or `deserialize` functions on them as appropriate.
|
||||||
|
|
||||||
use crate::core::hash::{Hash, Hashed};
|
use crate::core::hash::{DefaultHashable, Hash, Hashed};
|
||||||
use crate::keychain::{BlindingFactor, Identifier, IDENTIFIER_SIZE};
|
use crate::keychain::{BlindingFactor, Identifier, IDENTIFIER_SIZE};
|
||||||
use crate::util::read_write::read_exact;
|
use crate::util::read_write::read_exact;
|
||||||
use crate::util::secp::constants::{
|
use crate::util::secp::constants::{
|
||||||
|
@ -615,7 +615,7 @@ where
|
||||||
match elem {
|
match elem {
|
||||||
Ok(e) => buf.push(e),
|
Ok(e) => buf.push(e),
|
||||||
Err(Error::IOErr(ref _d, ref kind)) if *kind == io::ErrorKind::UnexpectedEof => {
|
Err(Error::IOErr(ref _d, ref kind)) if *kind == io::ErrorKind::UnexpectedEof => {
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
}
|
}
|
||||||
|
@ -706,7 +706,7 @@ pub trait FixedLength {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for types that can be added to a PMMR.
|
/// Trait for types that can be added to a PMMR.
|
||||||
pub trait PMMRable: Writeable + Clone + Debug {
|
pub trait PMMRable: Writeable + Clone + Debug + DefaultHashable {
|
||||||
/// The type of element actually stored in the MMR data file.
|
/// The type of element actually stored in the MMR data file.
|
||||||
/// This allows us to store Hash elements in the header MMR for variable size BlockHeaders.
|
/// This allows us to store Hash elements in the header MMR for variable size BlockHeaders.
|
||||||
type E: FixedLength + Readable + Writeable;
|
type E: FixedLength + Readable + Writeable;
|
||||||
|
@ -721,7 +721,7 @@ pub trait PMMRIndexHashable {
|
||||||
fn hash_with_index(&self, index: u64) -> Hash;
|
fn hash_with_index(&self, index: u64) -> Hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Writeable> PMMRIndexHashable for T {
|
impl<T: DefaultHashable> PMMRIndexHashable for T {
|
||||||
fn hash_with_index(&self, index: u64) -> Hash {
|
fn hash_with_index(&self, index: u64) -> Hash {
|
||||||
(index, self).hash()
|
(index, self).hash()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use self::core::core::hash::Hash;
|
use self::core::core::hash::{DefaultHashable, Hash};
|
||||||
use self::core::core::pmmr::{self, Backend};
|
use self::core::core::pmmr::{self, Backend};
|
||||||
use self::core::core::BlockHeader;
|
use self::core::core::BlockHeader;
|
||||||
use self::core::ser;
|
use self::core::ser;
|
||||||
|
@ -25,6 +25,8 @@ use std::path::Path;
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct TestElem(pub [u32; 4]);
|
pub struct TestElem(pub [u32; 4]);
|
||||||
|
|
||||||
|
impl DefaultHashable for TestElem {}
|
||||||
|
|
||||||
impl FixedLength for TestElem {
|
impl FixedLength for TestElem {
|
||||||
const LEN: usize = 16;
|
const LEN: usize = 16;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ use std::{cmp, thread};
|
||||||
use crate::chain;
|
use crate::chain;
|
||||||
use crate::common::stats::{StratumStats, WorkerStats};
|
use crate::common::stats::{StratumStats, WorkerStats};
|
||||||
use crate::common::types::{StratumServerConfig, SyncState};
|
use crate::common::types::{StratumServerConfig, SyncState};
|
||||||
|
use crate::core::core::hash::Hashed;
|
||||||
use crate::core::core::verifier_cache::VerifierCache;
|
use crate::core::core::verifier_cache::VerifierCache;
|
||||||
use crate::core::core::Block;
|
use crate::core::core::Block;
|
||||||
use crate::core::{pow, ser};
|
use crate::core::{pow, ser};
|
||||||
|
|
|
@ -21,6 +21,7 @@ use std::fs;
|
||||||
use chrono::prelude::Utc;
|
use chrono::prelude::Utc;
|
||||||
use croaring::Bitmap;
|
use croaring::Bitmap;
|
||||||
|
|
||||||
|
use crate::core::core::hash::DefaultHashable;
|
||||||
use crate::core::core::pmmr::{Backend, PMMR};
|
use crate::core::core::pmmr::{Backend, PMMR};
|
||||||
use crate::core::ser::{
|
use crate::core::ser::{
|
||||||
Error, FixedLength, PMMRIndexHashable, PMMRable, Readable, Reader, Writeable, Writer,
|
Error, FixedLength, PMMRIndexHashable, PMMRable, Readable, Reader, Writeable, Writer,
|
||||||
|
@ -903,6 +904,8 @@ fn load(pos: u64, elems: &[TestElem], backend: &mut store::pmmr::PMMRBackend<Tes
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
struct TestElem(u32);
|
struct TestElem(u32);
|
||||||
|
|
||||||
|
impl DefaultHashable for TestElem {}
|
||||||
|
|
||||||
impl FixedLength for TestElem {
|
impl FixedLength for TestElem {
|
||||||
const LEN: usize = 4;
|
const LEN: usize = 4;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue