2

I am trying to print some data for debugging purposes when I encountered an error. You can have a look at the full code here.

The error occurred when I tried to print data returned from the prepare_hash_data() function, which returns a Vec<u8>.

let data = self.prepare_hash_data()?;      // returns Vec<u8>
println!("{}", String::from_utf8(data)?);  // error here
let mut hasher = Sha256::new();
hasher.input(&data[..]);
println!("{}", hasher.result_str().as_str());
self.hash = hasher.result_str();

The prepare_hash_data() function is given below. Other details are omitted. Simply, it is just a function that returns Vec<u8>

fn prepare_hash_data(&self) -> Result<Vec<u8>, failure::Error> {
        let content = (
            self.hash_prev_block.clone(),
            self.transactions.clone(),
            self.timestamp,
            DIFFICULTY,
            self.nonce
        );
        let bytes = bincode::serialize(&content)?;
        Ok(bytes)
    }

The error given is

error[E0382]: borrow of moved value: `data`
  --> src/block.rs:63:23
   |
60 |         let data = self.prepare_hash_data()?;
   |             ---- move occurs because `data` has type `Vec<u8>`, which does not implement the `Copy` trait
61 |         println!("{}", String::from_utf8(data)?);
   |                                          ---- value moved here
62 |         let mut hasher = Sha256::new();
63 |         hasher.input(&data[..]);
   |                       ^^^^ value borrowed here after move

I tried the following ways

  • Implementing Copy trait. But, Vec<u8> can't have the Copy trait as described here.

  • Looking at E0382 given in the error message, there are two ways suggested.

    • Using a reference, we can let another function borrow the value without changing its ownership.

      • But how should I use reference in this example?
      • Should I change the function signature to something like this fn prepare_hash_data(&self) -> Result<&Vec<u8>, failure::Error>?
    • With Rc, a value cannot be owned by more than one variable.

      • Don't know how to implement.
  • I tried cloning the data by println!("{}", String::from_utf8(data.clone())?);

    • But, it gives another error backtrace::backtrace::trace_unsynchronized
    • For the full error log, click here

What should be the correct approach to printing some data that can't be copied or cloned without moving it for later usage in subsequent lines?

I did look at the following solutions but can't relate the answer.

tanmoy
  • 1,276
  • 1
  • 10
  • 28
  • See https://stackoverflow.com/q/41034635/3004881 : you shouldn't need to allocate a `String` just to print it, when a `&str` is just as printable. But the error you saw in #3 is surprising: if you still get that error, you should [edit] to post the full details of that error, and a little more of your code to show where your `Vec` came from. Is there something special about `self`? – Dan Getz Feb 19 '23 at 13:12
  • I am trying to print `Vec`, similar to the string. Full code is available [here](https://goonlinetools.com/snapshot/code/#mlx53xipih8brmo2tysei5). And full error log for #3 [here](https://goonlinetools.com/snapshot/code/#ge3y48vgz4igwva1lmmdqk). Updated question as well. – tanmoy Feb 19 '23 at 14:19
  • Note that this question is flawed; what you probably actually try to do is to convert the `Vec` to a hex string and then print this one. Maybe look here: https://programming-idioms.org/idiom/175/bytes-to-hex-string/2635/rust – Finomnis Feb 19 '23 at 14:54
  • Closing as "needs clarity" because that detail makes most of my answer irrelevant. This is the prime example of an XY problem. – Finomnis Feb 19 '23 at 14:55
  • Details are given for the sole purpose of giving clarity. Just printing `Vec` was not the goal, rather it's usage in any function that `moves` data. Rephrasing the question **What should be the correct approach to printing some data that can't be copied or cloned without moving it for later usage in subsequent lines?** And the data can be `non-utf8`. Does a different approach to an answer make a question flawed? And details were added later upon request. – tanmoy Feb 19 '23 at 20:16

1 Answers1

3

I am trying to print some data for debugging purposes

The code you provided:

println!("{}", String::from_utf8(data)?);

Is not the correct way to debug-print data.

For one, String::from_utf8 consumes the data, destroying it in the process. Further, your data most likely isn't valid UTF8 data, so String::from_utf8 will only throw an error.

Use debug printing instead, it works out of the box:

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

 


I retract my previous answer because it partially missed your problem.

It still contained valuable information, so I have kept it below. If someone disagrees, feel free to delete it together with this sentence.

 

--Previous Answer--

Your problem is that String::from_utf8 consumes its argument, meaning, data cannot be accessed any more afterwards.

Here is your problem in a more compact example:

fn main() {
    let data: Vec<u8> = "Hello!".as_bytes().to_vec();

    // Consumes `data`
    println!("{}", String::from_utf8(data).unwrap());

    // `data` cannot be accessed any more, as it got moved
    // into `String::from_utf8`
    println!("Length of vector: {}", data.len());
}
error[E0382]: borrow of moved value: `data`
 --> src/main.rs:9:38
  |
2 |     let data: Vec<u8> = "Hello!".as_bytes().to_vec();
  |         ---- move occurs because `data` has type `Vec<u8>`, which does not implement the `Copy` trait
...
5 |     println!("{}", String::from_utf8(data).unwrap());
  |                                      ---- value moved here
...
9 |     println!("Length of vector: {}", data.len());
  |                                      ^^^^^^^^^^ value borrowed here after move
  |
help: consider cloning the value if the performance cost is acceptable
  |
5 |     println!("{}", String::from_utf8(data.clone()).unwrap());
  |                                          ++++++++

In your case this can be easily fixed, though. The reason it consumes the data is because String is an owning variable. It owns its data, meaning, if you create it from a Vec, it stores the Vec data internally.

There is another type: &str, a string slice. It's very similar to String, just that it doesn't own its data, but merely references it. That means you can create it without destroying data:

fn main() {
    let data: Vec<u8> = "Hello!".as_bytes().to_vec();

    // Borrows `data`
    println!("{}", std::str::from_utf8(&data).unwrap());

    // `data` is still accessible because it was only borrowed
    println!("Length of vector: {}", data.len());
}
Hello!
Length of vector: 6

That said, the given solution is only a workaround. The proper way to fix this is to implement Display.

As your data object clearly has more meaning than just a bunch of bytes, create a proper struct that represents its meaning.

There are two ways I would consider:

  • A newtype struct, like struct MyData(Vec<u8>), for which you can implement Display.
  • A String. As you convert it to a string anyway, why not just make it a string right away, as the return value of prepare_hash_data? Note that if your reason to have it a Vec is that it is binary data, then you shouldn't convert it to a string via from_utf8, as it's not UTF-8 data. If it is valid UTF-8 data, however, use a String right away, not a Vec<u8>. And String already implements Display and can be printed without further conversion.
Finomnis
  • 18,094
  • 1
  • 20
  • 27
  • 1
    The third case hints that the data are _not_ valid UTF-8, in fact, since OP gets a `FromUtf8Error`. Therefore, they can't use `String` (or `&str`) at all. – Cerberus Feb 19 '23 at 14:38
  • @Cerberus OP probably meant to convert to a hex string, which something else than `from_utf8`. – Finomnis Feb 19 '23 at 14:49
  • @Cerberus Where did you see the `FromUtf8Error`? – Finomnis Feb 19 '23 at 19:51
  • Got directions from this answer to solve this issue by writing a helper function that takes reference of `Vec` and doesn't own it ensuring later usage. – tanmoy Feb 19 '23 at 20:24
  • `@Cerberus Where did you see the FromUtf8Error? ` Added [error log](https://goonlinetools.com/snapshot/code/#ge3y48vgz4igwva1lmmdqk) later to the question that got generated when tried using `println!("{}", String::from_utf8(data.clone())?)`. – tanmoy Feb 19 '23 at 20:27
  • 1
    @MuhtasimUlfatTanmoy Don't use `&Vec`, use `&[u8]` instead, it's 100% compatible and more general – Finomnis Feb 19 '23 at 21:29
  • Thank you so much for your answer as it clarified lots of concepts related to the question which is valuable. Now, as per your suggestion to `Don't use &Vec, use &[u8]`, why it is better as both references are `borrowed type` though one point to `owned Vec` and another to `borrowed [u8]`? Where `&Vec` falls short in terms of compatibility? – tanmoy Feb 20 '23 at 06:05
  • Is this the [reason](https://stackoverflow.com/questions/40006219/why-is-it-discouraged-to-accept-a-reference-string-vec-or-box-as-a-function) for your suggestion? – tanmoy Feb 20 '23 at 06:25
  • 1
    `&Vec` is not owned; `Vec` is. `&Vec` is a reference to an owned vector, and so is `&[u8]`. You can pass `&Vec` into a function that expects `&[u8]`. All the extra functionality that a `Vec` reference brings isn't available if it isn't `&mut`; so you don't gain any extra functionality from it. To the cost that your function is then **only** compatible with vectors. For `&[u8]`, it accepts arguments of the types `&Vec`, `&[u8;10]` (references to arrays) or `&[u8]` directly. You gain generality at no drawback. – Finomnis Feb 20 '23 at 18:25
  • 1
    It's called the [robustness principle](https://en.wikipedia.org/wiki/Robustness_principle) - if you pass data somewhere, do it as conservative as possible. If you receive data (as in an argument), do it as liberal as possible. And `&[u8]` accepts a strict superset of `&Vec`, so it is the better choice for a parameter, as it is more liberal. – Finomnis Feb 20 '23 at 18:30