홈강좌Rust 입문·중급 47편 중 21편째
Rust 슬라이스 — 배열과 문자열의 일부만 빌린다, 한글은 3바이트씩
Rust 슬라이스는 &scores[1..4] 처럼 배열이나 문자열의 일부만 빌린 참조다. 문자열 슬라이스의 번호는 글자가 아니라 바이트다. 한 글자가 3바이트인 한글을 글자 중간에서 자르면 실행 중에 패닉한다. 글자 단위로 다룰 때는 chars() 를 쓴다.
01 배열의 일부만 빌린다
9편의 점수 배열에서 가운데 셋만 꺼내 보자.
fn main() {
let scores = [72, 85, 90, 64, 88];
let middle = &scores[1..4];
let first_two = &scores[..2];
let last_two = &scores[3..];
println!("{middle:?}");
println!("{first_two:?}");
println!("{last_two:?}");
} Compiling scores v0.1.0 (~/projects/scores)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/scores`
[85, 90, 64]
[72, 85]
[64, 88]&scores[1..4]는 1번부터 4번 바로 앞까지(1·2·3번)를 빌린다. 범위 쓰는 법은 14편과 같다.- 시작을 비우면(
..2) 처음부터, 끝을 비우면(3..) 끝까지다. - 앞에
&가 붙는 것은 18편의 빌림이라서다. 값을 복사하지 않고 원래 배열의 그 구간을 가리킨다.
02 문자열도 같은 방법으로 자른다
fn main() {
let english = "hello world";
println!("{}", &english[0..5]);
let korean = "안녕하세요";
println!("{}", korean.len());
println!("{}", &korean[0..3]);
println!("{}", &korean[0..6]);
} Compiling text v0.1.0 (~/projects/text)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.16s
Running `target/debug/text`
hello
15
안
안녕영어 "hello world" 의 [0..5] 는 hello 다. 그런데 한글을 보면 이상하다.
"안녕하세요"는 다섯 글자인데len()이 15 다.[0..3]이안한 글자,[0..6]이안녕두 글자다.
문자열 번호가 글자가 아니라 바이트이기 때문이다. Rust 문자열은 UTF-8 로 저장되는데, 영어 글자는 1바이트, 한글 글자는 3바이트를 쓴다. 그래서 다섯 글자가 15바이트이고, 한 글자를 자르려면 3 단위로 잘라야 한다. 18편에서 len() 이 "정확히는 바이트 수" 라고 한 것이 이 말이다.
03 글자 중간을 자르면 패닉한다
영어처럼 [0..2] 로 두 글자를 잘라 보자.
fn main() {
let korean = "안녕하세요";
let part = &korean[0..2];
println!("{part}");
} Compiling text v0.1.0 (~/projects/text)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/text`
thread 'main' (213046763) panicked at src/main.rs:3:23:
end byte index 2 is not a char boundary; it is inside '안' (bytes 0..3) of `안녕하세요`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace컴파일은 됐지만 실행 중에 멈췄다(panicked). 둘째 줄이 이유다. 끝 번호 2 가 글자 경계가 아니다(not a char boundary). '안' 이 0~3 바이트를 차지하는데 그 한가운데를 잘랐다. 괄호 안의 숫자는 실행할 때마다 바뀌니 신경 쓰지 않아도 된다.
반쪽짜리 글자는 글자가 아니니, Rust 는 깨진 글자를 만드는 대신 멈춘다. 컴파일러가 미리 막지 못하는 것은 자르는 번호가 실행 중에 정해질 수 있기 때문이다.
04 실습 — 글자 단위로는 chars()
글자 수를 세거나 한 글자씩 다룰 때는 chars() 를 쓴다.
fn main() {
let korean = "안녕하세요";
println!("{}", korean.chars().count());
for c in korean.chars() {
println!("{c}");
}
} Compiling text v0.1.0 (~/projects/text)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.21s
Running `target/debug/text`
5
안
녕
하
세
요chars() 는 문자열을 char(7편) 하나씩으로 나눈다. .count() 를 붙이면 글자 수 5 가 나오고, for 로 돌면 한 글자씩 꺼낸다. 바이트를 따질 필요가 없다.
정리하면, 영어만 다루는 짧은 코드라면 [0..5] 로 잘라도 되지만, 한글이 섞일 수 있으면 chars() 로 글자 단위로 다룬다. 여기까지가 소유권 구간이다. 다음 편부터는 여러 값을 이름 붙여 묶는 구조체로, 데이터를 다루는 법을 본다.