Shake128 method not found

Hello, I want to use Shake128 to hash an array with extendable output. Unfortunately, the documentation for crate sha3 is not detailed. I tried the following code:

use sha3::{Digest, Shake128};

fn main() {
    let mut hasher = sha3::Shake128::default();
    let input_bytes: [u8; 4] = [1, 2, 3, 255];
    hasher.write_all(&input_bytes);
    let mut output_bytes = [0u8; 32];
    let output_len: usize = hasher.xof_result().read(output_bytes);

    println!("{:?}", output_len);
}

It does not compile and raises the following error:

error[E0599]: no method named `write_all` found for struct `sha3::Shake128` in the current scope
    --> src/lib.rs:6:12
     |
6    |     hasher.write_all(&input_bytes);
     |            ^^^^^^^^^ method not found in `sha3::Shake128`
     |
    ::: /home/a3k12/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/io/mod.rs:1366:8
     |
1366 |     fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
     |        ---------
     |        |
     |        the method is available for `std::boxed::Box<sha3::Shake128>` here
     |        the method is available for `std::sync::Arc<sha3::Shake128>` here
     |        the method is available for `std::rc::Rc<sha3::Shake128>` here
     |
     = help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
     |
1    | use std::io::Write;
     |

When I read the documentation, the method does seem to be available. I don't understand why I should pack my Shake128 in a box to be able to call that method -- it is possible that I am missing some obvious Rust concept here since I'm a beginner.

Thanks!

Have you tried importing std::io::Write like the error message suggests?

1 Like

Indeed, it was that simple, sorry for the question. Thank you!

I was confused by other error messages about xof_result(), but actually the solution is similar there: add use digest::{ExtendableOutputDirty, XofReader}; too.

This is not very intuitive as a newcomer. Shouldn't we expect a crate to import all its necessary dependencies, instead of having to discover by trial/error? It seems strange to have dozens of use statements at the beginning of my module, for functionalities that I only use indirectly.

I agree it can be pretty annoying when you're starting out. I think it's something you get used to over time though, and there are probably good reasons for why it is the way it is.

sha3 seems to re-export digest, so you should be able to write use sha3::digest::{ExtendableOutputDirty, XofReader}; instead of depending on and using digest directly. If you don't know to look for it, it can be pretty easy to miss, since the search on docs.rs won't show any of the types from digest. Instead you have to find it on the crate's front page: https://docs.rs/sha3/0.9.1/sha3/#reexports

Another thing crates can do is define a prelude-like module, so you'd write use sha3::prelude::*;, which would contain all the most important imports of the crate. It doesn't seem to be a very common pattern though, and I'm sure many programmers prefer explicit imports.

This is one of the things that makes it possible for crates to extend types defined elsewhere. If a library defines something like this:

impl<T> MyTrait for T {
    fn do_something(&self) { /* ... */ }
}

You don’t want every method you’ve defined called do_something to suddenly have a name conflict when you add the dependency. The rule about importing traits means that you have to opt-in to any potential name conflicts.

1 Like

Indeed, thank you all for your replies!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.