6

I'm having some issues understanding the concept of traits in Rust. I'm trying to encode a simple hex value to Base64 but with no luck, here is my code (with an example of string to Base64 also)

extern crate serialize;

use serialize::base64::{ToBase64, STANDARD};
use serialize::hex::{FromHex, ToHex};

fn main () {
  let stringOfText = "This is a String";
  let mut config = STANDARD;

  println!("String to base64 = {}", stringOfText.as_bytes().to_base64(config));

  // Can't figure out this 

The solution provided by Vladimir works for 0x notated hex values. Now I'm looking to convert a hex value that is represented in a string:

extern crate serialize;

use serialize::base64::{ToBase64, STANDARD};
use serialize::hex::{FromHex, ToHex};
fn main () {
  let stringOfText = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d";
  let mut config = STANDARD;

  println!("String to base64 = {}", stringOfText.from_hex().from_utf8_owned().as_bytes().to_base64(config));

  // result should be: SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t

}

from_hex() gives me a Vec<u8> and .to_base64() is expecting a buffer of u8, first I thought to convert the Vec<u8> to string and then use the as_bytes() to get the buffer, so far still no luck.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
David Dias
  • 1,792
  • 3
  • 16
  • 28
  • The only implementor of `ToBase64` trait is `&[u8]` (http://doc.rust-lang.org/serialize/base64/trait.ToBase64.html) , so you will need to convert that number to it before you can use `to_base64()`. – snf Oct 03 '14 at 19:37
  • Thank you @snf , just updated to this `let hex = 0x49276d2;` `let integer = hex as u8;` `println!("Hex to base64 = {}", integer.to_base64(config));` but still no luck – David Dias Oct 03 '14 at 19:44
  • I don't get what you want to do, do you want to base64_encode "0x49276d2" or "\x04\x92\x76\xd2" or something else? – snf Oct 04 '14 at 00:53
  • 2
    @david-dias Could you please change the accepted answer to the one that works in 2017? – Jeff Allen Jul 21 '17 at 10:41
  • Done, thanks for the ping! :) – David Dias Jul 22 '17 at 04:42

4 Answers4

15

It is now 2017, and rustc-serialize is now deprecated as well (see here). The new big serialization library is called serde but I think that's a little heavy for this problem.

Here's a quick solution that manually converts from the String of hex to a Vec<u8> and then uses this to convert to a base64-encoded String.

I am not convinced that this for loop is anywhere near the best solution for converting a String of hex into a Vec<u8>, but I'm fairly new to Rust and this is all I've got. Comments/improvements are appreciated.

(Hey, wait a minute, I recognize these encoded strings, are you doing cryptopals?)

extern crate base64;
use std::u8;
use base64::{Engine as _, engine::general_purpose};

pub fn hex_to_base64(hex: String) -> String {

    // Make vector of bytes from octets
    let mut bytes = Vec::new();
    for i in 0..(hex.len()/2) {
        let res = u8::from_str_radix(&hex[2*i .. 2*i+2], 16);
        match res {
            Ok(v) => bytes.push(v),
            Err(e) => println!("Problem with hex: {}", e),
        };
    };

    general_purpose::STANDARD.encode(&bytes) // now convert from Vec<u8> to b64-encoded String
}
firechant
  • 886
  • 1
  • 14
  • 22
10

Rust is currently evolving, and the accepted answer does not work properly any more. Specifically, the extern crate statement does not allow quoted parameters, and the name of the rustc-serialize crate is now rustc_serialize.

The following code correctly base64 encodes the string, yielding the same result as the accepted answer (rustc 1.3.0):

extern crate rustc_serialize as serialize;

use serialize::base64::{self, ToBase64};
use serialize::hex::FromHex;

fn main() {
    let input = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d";
    let result = input.from_hex().unwrap().to_base64(base64::STANDARD);
    println!("{}", result);
}

Result:

SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t

Jonatan
  • 2,734
  • 2
  • 22
  • 33
4

For your updated question, try this:

extern crate "rustc-serialize" as serialize;

use serialize::base64::{self, ToBase64};
use serialize::hex::FromHex;

fn main() {
    let input = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d";
    let result = input.from_hex().unwrap().as_slice().to_base64(base64::STANDARD);
    println!("{}", result);
}

(Note that serialize is now in an external crate, see rustc-serialize. Thanks to gsingh2011.)

from_hex returns a Result<Vec<u8>, FromHexError>, which is unwrapped to a Vec<u8>, then used as a &[u8] with as_slice().

Result is a type that is either Ok or Err and is usually returned from methods that can fail to compute a result. In the above case unwrap is used to get to the Ok value. If the result wasn't Ok, it fails (try it by removing one of the letters from the string).

Depending on what your program does, the error case should be handled explicitly using a match:

match input.from_hex() {
    Ok(result) => println!("Yay: {}", result.as_slice().to_base64(base64::STANDARD)),
    Err(error) => println!("Nay: {}", error)
}

Or if you just want to continue with a default value, you can use unwrap_or:

println!("{}", input.from_hex().unwrap_or(vec!()).as_slice().to_base64(base64::STANDARD));

See documentation for Result for other methods.

Community
  • 1
  • 1
robinst
  • 30,027
  • 10
  • 102
  • 108
  • Thank you so much Robinst! So if I understand correctly, A Rust best practice is to return a result and error inside a 'package' that then has to be unwrapped to get the actual result (or error in case of error?) – David Dias Oct 04 '14 at 09:19
  • 1
    Yes, I added more explanation for that in the answer. Other languages sometimes use exceptions for this, or multiple return values. Rust has a type with nice methods, that allows the caller to handle it one way or another. – robinst Oct 04 '14 at 09:34
  • Note that the base64 conversion functions have moved to an external crate: https://github.com/rust-lang/rustc-serialize – gsgx Jan 28 '15 at 16:07
1

I'm not sure what do you mean - convert hex value to base64 (fwiw there is no such thing as hex value - it's just another form of the same literal number). Assuming that you want to convert bytes which make up int value, you can do something like this:

extern crate serialize;

use std::mem;
use serialize::base64::{mod, ToBase64};

fn decompose_int(n: int, buf: &mut [u8]) {
    assert!(mem::size_of::<int>() == buf.len());
    for i in range(0, buf.len()) {
        buf[i] = ((n >> i*8) & 0xFF) as u8;
    }
}

fn main() {
    let mut bytes = [0u8, ..8];
    let x = 0x1122334455667788;
    decompose_int(x, &mut bytes);
    println!("{}", bytes.to_base64(base64::STANDARD));
}

(try it here)

Here decompose_int function serializes an int value into a slice of bytes in little endian format. Then this slice is converted to base64 as usual.

If you need big endian representation, you will need to change buf[i] to buf[buf.len()-1-i].

Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • Thank you :) . Exactly, I just want to understand how to make that first step in Rust. Thank you for your solution it definitely works for 0x values (as I've asked.) Now I'm trying to use a String to represent a hex value, which requires me to convert it to a vector of bytes (see updates on top). – David Dias Oct 04 '14 at 07:57
  • do you think it's a good idea to modify parameters - `buf[i] = `? Instead there must be another variable `let buf1 = buf; buf1[i] = ...` – Incerteza Oct 27 '14 at 06:16
  • @AlexanderSupertramp, why? I see no reason for the temporary variable. – Vladimir Matveev Oct 27 '14 at 06:18
  • in general, modifying parameters is bad, haven't your heard of this? – Incerteza Oct 27 '14 at 07:07
  • @AlexanderSupertramp, this *may* be so when you're modifying parameters themselves (e.g. in this case it would be if you replaced one slice with the other, but it is impossible since `buf` variable is not mutable), and I'd argue that even this is debatable in Rust. Moreover, here the slice itself is mutable, and its sole reason of existence is to allow mutating its elements. Adding a local variable brings nothing more than confusion. Sorry, but applying such rules without thinking is a very wrong approach. – Vladimir Matveev Oct 27 '14 at 08:11
  • "this may be so when you're modifying parameters themselves" -> isn't this what I'm talking about? – Incerteza Oct 27 '14 at 11:24
  • No. Here the parameter itself is not modified, and it can't be, because it is not `mut buf: &mut [u8]`. `buf` is a mutable slice and it is used to mutate elements which are contained somewhere else. – Vladimir Matveev Oct 27 '14 at 11:48