How to implement the trait std::convert::AsRef<[u8]> for generic structure

Hello community. I'm begginer with Rust so I aprecciate a detailed solution. If you think I should read some part of the documentation would be nice to know it. I'm trying to make a merkle tree of blockchain transactions. I already have used some traits but I'm having problems to understand how to use this. I dont know how to resolve the error. Bellow is my code and the error.

Thank you.

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Transaction {
	id: Vec<u8>,
	vin: Vec<TXInput>,
	vout: Vec<TXOutput>
}

impl Transaction {
	pub fn new(to: String, mut data: String) -> Transaction {
        //Some code here
	}
}

static digest: &'static Algorithm = &SHA512;
let tree = MerkleTree::from_vec(digest, transactions); // transactions: Vec<transaction::Transaction>

> the trait bound `transaction::Transaction: std::convert::AsRef<[u8]>` is not satisfied
> the trait `std::convert::AsRef<[u8]>` is not implemented for `transaction::Transaction`
> note: required because of the requirements on the impl of `merkle::hashutils::Hashable` for `transaction::Transaction`
> note: required by `merkle::merkletree::MerkleTree::<T>::from_vec`rustc(E0277)
> utils.rs(46, 15): the trait `std::convert::AsRef<[u8]>` is not implemented for `transaction::Transaction`

If I'm reading it correctly, from_vec wants AsRef<[u8]> which is a way of saying it wants Vec<u8>, or &[u8] slice, or some equivalent.

It can't be Transaction, because it doesn't implement that AsRef trait, and it couldn't, because it's not a single contiguous slice of bytes (you have 3 separate vecs in there).

You probably need a different interface which will allow you to feed your data bit by bit.

I have implemented this and It doesn't show me the error. Do you think It's a right solution?:

impl AsRef<[u8]> for Transaction {
    fn as_ref(&self) -> &[u8] {
		let encoded = bincode::serialize(self).unwrap();
		let c: &[u8] = &encoded;
		c		
    }
}

Since this does compile, it means that the &[u8] is borrowing from the original struct (otherwise the borrow checker would reject the function as returning the reference to the local). It means that you aren't serializing Vecs content, but rather simply reinterpret the bytes of the struct itself. I can't check it right now, since the bincode isn't available of playground, but you could try to print transaction.as_ref() and see if I'm right.

@Cerberuser as you mentioned I have this error:

let c: &[u8] = &encoded;
cannot return value referencing local variable encoded
returns a value referencing data owned by the current functionrustc

Is there a way to solve it?

For AsRef<&[u8]> to work, you must have a contiguous sequence of bytes somewhere in the struct. It can't be create in the as_ref() method, since anything created in the method and not returned from it is lost, and the reference would dangle.

1 Like

References can't exist without an owned version of the value having permanent storage somewhere. You can't create encoded, then throw it away (at the end of the function), and then give out a reference to a destroyed variable.

Basically, if a method returns &, the data has to exist in the struct already, before the method was called (there are sometimes workarounds with Box::leak, or once_cell, but they don't make sense in your case).

This trait is supposed to view the data that already existed, not generate the data.

As I mentioned before, the from_vec is a dead-end in your case. You need to find a different interface, which like digest functions with .update(), allows adding data bit by bit.

1 Like

Okey. So I added a new field in the struct:

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Transaction {
    id: Vec<u8>,
    vin: Vec<TXInput>,
    vout: Vec<TXOutput>,
    encoded: Vec<u8>
}

And as_ref():

impl AsRef<[u8]> for Transaction {
    fn as_ref(&self) -> &[u8] {
		&self.encoded
    }
}

I'm not sure if I'll have problems with MerkleTree but for the moment It seems to be working.

Thank you!