10

I want to convert a string of characters (a SHA256 hash) to hex in Rust:

extern crate crypto;
extern crate rustc_serialize;

use rustc_serialize::hex::ToHex;
use crypto::digest::Digest;
use crypto::sha2::Sha256;

fn gen_sha256(hashme: &str) -> String {
    let mut sh = Sha256::new();
    sh.input_str(hashme);

    sh.result_str()
}

fn main() {
    let hash = gen_sha256("example");

    hash.to_hex()
}

The compiler says:

error[E0599]: no method named `to_hex` found for type `std::string::String` in the current scope
  --> src/main.rs:18:10
   |
18 |     hash.to_hex()
   |          ^^^^^^

I can see this is true; it looks like it's only implemented for [u8].

What am I to do? Is there no method implemented to convert from a string to hex in Rust?

My Cargo.toml dependencies:

[dependencies]
rust-crypto = "0.2.36"
rustc-serialize = "0.3.24"

edit I just realized the string is already in hex format from the rust-crypto library. D'oh.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
leshow
  • 1,568
  • 2
  • 15
  • 30
  • 1
    "method to convert from string to hex" - what would you like the result of "abc", "日本国", and "♩♪♫" to be as hex? – Shepmaster Dec 15 '14 at 21:13
  • The problem is that in Rust, a `String` or `&str` can contain UTF-8 values, which encompass all the values above. The answer you posted indicates that you are OK with "The hex values of the UTF-8 bytes". – Shepmaster Dec 15 '14 at 21:58
  • rustc-serialize is deprecated in favour or serde. For hexing and unhexing things, looks like the hex crate is a better idea. – enigmaticPhysicist Sep 14 '22 at 05:25

5 Answers5

19

I will go out on a limb here, and suggest that the solution is for hash to be of type Vec<u8>.


The issue is that while you can indeed convert a String to a &[u8] using as_bytes and then use to_hex, you first need to have a valid String object to start with.

While any String object can be converted to a &[u8], the reverse is not true. A String object is solely meant to hold a valid UTF-8 encoded Unicode string: not all bytes pattern qualify.

Therefore, it is incorrect for gen_sha256 to produce a String. A more correct type would be Vec<u8> which can, indeed, accept any bytes pattern. And from then on, invoking to_hex is easy enough:

hash.as_slice().to_hex()
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 1
    This is absolutely the correct solution. [rustc itself](http://doc.rust-lang.org/rustc/util/sha2/struct.Sha256.html) does this for the sha256 it generates, in fact. – Shepmaster Dec 16 '14 at 18:17
  • I'm new to rust, therefore I'm not too familiar with conversion between types. I returned the 'String' type because the library I'm using returns that type for the hash. How might I rewrite this gen_sha256 so it returns a Vec? (posted function in OP) – leshow Dec 17 '14 at 16:09
  • Which library do you use? And if it does use a `String`, are you sure it is not already HEX encoded? From `String` to `Vec` you can use `as_bytes().collect()`, but I am worried that there might be a deeper issue. (For example, in the `rustc` library [linked above by Shepmaster](http://doc.rust-lang.org/src/rustc_back/sha2.rs.html#268-270), to return a `String` it actually calls the version that returns a `Vec` and then hex encode it: `self.result_bytes().to_hex().to_string()`, where `result_bytes` returns `Vec`) – Matthieu M. Dec 17 '14 at 16:55
  • the library actually does return it as a hex encoded string. in the code that I was writing I actually wanted to convert a regular string into a hex string, but I just used the sha256 hash as my test string... forgetting that a sha256 hash is already in hex format lol. – leshow Dec 17 '14 at 17:22
  • I'm actually converting this string to hex format: let random_chars: Vec = task_rng().gen_iter::().take(take_num).collect(); I took your advice and made it a Vec – leshow Dec 17 '14 at 17:24
  • I get this error "no method named `to_hex` found for type `&[u8]` in the current scope" when using to_hex. What am I doing wrong here? – misnomer___ May 31 '18 at 22:39
  • 1
    @misnomer___: Most likely, you forgot to bring the trait into scope. In Rust, inherent methods can be used without any further fuss, but trait methods require that the trait be in scope (otherwise, it's unclear which trait they came from). Insert `use rustc_serialize::hex::ToHex;` and it should work. – Matthieu M. Jun 01 '18 at 06:38
5

It appears the source for ToHex has the solution I'm looking for. It contains a test:

#[test]
pub fn test_to_hex() {
    assert_eq!("foobar".as_bytes().to_hex(), "666f6f626172");
}

My revised code is:

let hash = gen_sha256("example");

hash.as_bytes().to_hex()

This appears to work. I will take some time before I accept this solution if anyone has an alternative answer.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
leshow
  • 1,568
  • 2
  • 15
  • 30
3

A hexadecimal representation can be generated with a function like this:

pub fn hex_push(buf: &mut String, blob: &[u8]) {
    for ch in blob {
        fn hex_from_digit(num: u8) -> char {
            if num < 10 {
                (b'0' + num) as char
            } else {
                (b'A' + num - 10) as char
            }
        }
        buf.push(hex_from_digit(ch / 16));
        buf.push(hex_from_digit(ch % 16));
    }
}

This is a tad more efficient than the generic radix formatting implemented currently in the language.

Here's a benchmark:

test bench_specialized_hex_push   ... bench:          12 ns/iter (+/- 0) = 250 MB/s
test bench_specialized_fomat      ... bench:          42 ns/iter (+/- 12) = 71 MB/s
test bench_specialized_format     ... bench:          47 ns/iter (+/- 2) = 63 MB/s
test bench_specialized_hex_string ... bench:          76 ns/iter (+/- 9) = 39 MB/s
test bench_to_hex                 ... bench:          82 ns/iter (+/- 12) = 36 MB/s
test bench_format                 ... bench:          97 ns/iter (+/- 8) = 30 MB/s
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
ArtemGr
  • 11,684
  • 3
  • 52
  • 85
  • 1
    Presumably this is already implemented in a crate and every person who want to convert doesn't need to copy/paste this... – Shepmaster Jan 28 '18 at 16:09
  • I'm sorry, I didn't mean to cause offense. I'm genuinely curious — why is this code better than the crate which has the sole purpose of converting byte slices to hex? – Shepmaster Jan 28 '18 at 19:36
  • 1
    @Shepmaster, This code is simple enough to be educational and can be easily adopted to anyone's needs, easily fit into unanticipated abstractions. Benchmark is educational as well. On the other hand, spawning a third hex crate for something that simple would only bring namespace pollution and distract me from doing things that are more important. Hopefully this explains why it should be there and not in a crate. A question for you to consider: why giving other people advice they haven't asked for is better for you than getting more time for yourself and your own projects? – ArtemGr Jan 28 '18 at 23:33
  • 1
    I truly apologize for insulting/offending you, it was not my intent. However, I'm not talking to just you, but also to everyone else who comes to this answer later, so I'll state my point plainly and move on: this code is accurate, works and understanding it is useful. However, I believe that no one should use it *because* by copy-and-pasting it locally you are increasing your own maintenance burden. Instead, one should use [the hex crate](https://crates.io/crates/hex). In addition to having a maintainer, based on some quick benchmarks I did, the crate is faster than this code. – Shepmaster Jan 29 '18 at 00:19
  • 1
    @ArtemGr "A question for you to consider: why giving other people advice they haven't asked for is better for you than getting more time for yourself and your own projects?", a question for you, did you forget that you are on stackoverflow ? It's a collaborative site ! Comments are exactly here for "giving other people advice they haven't asked". – Stargateur Jan 29 '18 at 00:37
  • I'd like to similarly warn "everyone else who comes to this answer later" from spamming their code with unnecessary crates and abstractions. The more top-level abstractions you have, the more complex and less maintainable you project becomes. This is called over-design. Copying is fine when it's practical, see Rule of three. And you can learn more about the benefits of inline code at https://www.youtube.com/watch?v=5Nc68IdNKdg. – ArtemGr Jan 29 '18 at 08:58
  • You're missing the point, @Stargateur. Shepmaster tries to get in my head and tell me how I should spend my personal time (by making new crates or advertising the existing ones, etc). I gave him a personal question in return. The question was not about Stackoverflow, it was about Shepmaster specifically. As for the comments and advice, no-one likes it when it's presumptuous. I make everyone's life better by reminding Shepmaster that people need some personal space. – ArtemGr Jan 29 '18 at 09:41
  • @ArtemGr you're violating the 'rule of three' by putting this function verbatim in your code though. There are surely more than three people who need this function, therefore it should be refactored and moved to a single place. The code is very informational though, so thank you for that. – leshow Oct 27 '18 at 21:19
  • It might be a dangerous rationalization, @leshow. Next time you're writing a function you might think "_but surely there are more than three people who need this_", which in turn might prevent you from enjoying the flexibility of the Rule of three and YAGNI. – ArtemGr Oct 27 '18 at 22:12
2

Thanks to the user jey in the ##rust irc channel in freenode. You can just use the hex representation fmt provides,

>> let mut s = String::new();
>> use std::fmt::Write as FmtWrite; // renaming import to avoid collision
>> for b in "hello world".as_bytes() { write!(s, "{:02x}", b); }
()
>> s
"68656c6c6f20776f726c64"
>> 

or a bit silly one,

>> "hello world".as_bytes().iter().map(|x| format!("{:02x}", x)).collect::<String>()
"68656c6c6f20776f726c64"
han solo
  • 6,390
  • 1
  • 15
  • 19
2

Using the hex crate, it is very easy:

use hex;
println!("{}", hex::encode(String("some str")));
enigmaticPhysicist
  • 1,518
  • 16
  • 21