Rust는 기본적으로 UTF-8 중심으로 문자열을 처리한다.1 그러나 Windows는 euc-kr을 확장한 CP949 (MS949)와 Unicode로 한글을 처리하므로2 Windows에서는 영문, 숫자와 별개로 한글을 byte 단위로 처리할 때 2byte 또는 3byte의 문자가 혼재할 수 있다.

아래의 예제는 2byte (AnsiString) 한글만으로 Hex를 encode, decode 필요가 있을 때 참고할 만한 소스이다.

[dependencies]
encoding = "0.2.33"
hex = "0.4.2"
main.rs
use encoding::{label::encoding_from_whatwg_label, EncoderTrap, DecoderTrap};
use hex;

fn main() {
    let kor_eng_num = String::from("가나다 ABC 123");
    println!("{}", kor_eng_num);
    println!("---------------------");

    let mut vec = Vec::new();
    let string_list = kor_eng_num.split_whitespace();
    for s in string_list {
        println!("UTF8: {} / MS949: {}", hex::encode(s).to_uppercase(), split_hex(s.into()));
        vec.push(split_hex(s.into())); // s.into() -> &s
    }
    println!("---------------------");

    for hex_str in vec.iter() {
        println!("{} : {}", hex_str, hex2str(hex_str));
    }
}
fn hex2str(s: &str) -> &str {
    let euckr = encoding_from_whatwg_label("euc-kr").unwrap();
    let decode_string = euckr.decode(&hex::decode(s).unwrap(), DecoderTrap::Replace).unwrap();
    return string_to_static_str(decode_string);
}

fn split_hex(s: &str) -> &str { //&str -> String
    let euckr = encoding_from_whatwg_label("euc-kr").unwrap();
    //println!("{} / {:?}", euckr.name(), euckr.whatwg_name());

    let encode_string = euckr.encode(&s, EncoderTrap::Replace).unwrap();
    let hex_encode_string = hex::encode(encode_string).to_uppercase();
    return string_to_static_str(hex_encode_string);
}

// 메모리 누수가 발생하는 함수
fn string_to_static_str(s: String) -> &'static str {
    Box::leak(s.into_boxed_str())
}

/* 출력결과
가나다 ABC 123
---------------------
UTF8: EAB080EB8298EB8BA4 / MS949: B0A1B3AAB4D9
UTF8: 414243 / MS949: 414243
UTF8: 313233 / MS949: 313233
---------------------
B0A1B3AAB4D9 : 가나다
414243 : ABC
313233 : 123
*/
메모리 누수 함수 수정 대안

1.소유권을 그대로 넘기기

fn string_to_owned(s: String) -> String {
    s // 그대로 반환 (소유권 이동)
}

2.라이프타임(Lifetime 'a) 사용하기

struct MyStruct<'a> {
    name: &'a str,
}

fn process_str(s: &str) {
    let item = MyStruct { name: s };
    println!("{}", item.name);
}

3.공유 소유권(Arc 또는 Rc) 사용하기

use std::sync::Arc;

fn string_to_shared(s: String) -> Arc<str> {
    Arc::from(s.into_boxed_str())
}

// 사용
let shared = string_to_shared(String::from("hello"));
// 참조 카운트 증가, 마지막 참조가 사라지면 자동 해제
let reference = Arc::clone(&shared); 

4.런타임 상수가 필요하다면 (once_cell 또는 lazy_static)

use once_cell::sync::Lazy;

static GLOBAL_STR: Lazy<String> = Lazy::new(|| {
    String::from("런타임에 생성된 전역 문자열")
});

Reference

  1. rust-lang.org, “Strings and UTF-8”
  2. 나무위키, “CP949”