문자열과 슬라이스 다루기

RustBeginner
지금 연습하기

소개

Rust에서는 소유권을 가지며 크기를 늘릴 수 있는 텍스트에 String을 사용하고, 텍스트를 빌려 보는 뷰에는 &str을 사용합니다. 문자열 슬라이스는 문자를 복사하지 않고 문자열 전체 또는 지정한 바이트 범위를 볼 수 있습니다.

이 실습에서는 소유한 텍스트를 &str로 빌리는 함수에 전달하고, 빌린 명령 키워드를 만들며, UTF-8 텍스트를 지원되지 않는 방식으로 인덱싱하도록 컴파일러에 의도적으로 요청합니다. 그런 다음 유효한 경계를 사용하는 슬라이스로 수정합니다.

소유한 텍스트와 리터럴 텍스트를 &str로 빌리기

이 단계에서는 서로 다른 두 텍스트 소스를 빌린 문자열 슬라이스를 받는 하나의 함수에 전달합니다.

준비된 프로젝트로 이동한 후 소스 파일을 엽니다.

cd /home/labex/project/string-slices
nano src/main.rs

String 타입은 크기를 늘릴 수 있는 텍스트의 소유권을 가집니다. “문자열 슬라이스”라고 읽는 &str 타입은 유효한 UTF-8 텍스트를 빌려 보는 뷰입니다. "preview"와 같은 문자열 리터럴은 프로그램이 실행되는 동안 텍스트를 저장하므로 이미 빌린 문자열 슬라이스 타입입니다.

show_text(label: &str) 함수는 두 종류의 텍스트를 모두 볼 수 있습니다. /* Step 1: borrow command */만 &command로 바꿉니다.

    show_text(&command);

Rust는 빌린 &String을 함수가 요구하는 &str 뷰로 자동 처리합니다. 이 변환에서는 문자를 복사하거나 소유권을 이동하지 않습니다.

Ctrl+O로 저장하고 Enter 키를 누른 다음, Ctrl+X로 종료합니다. 프로그램을 실행합니다.

cargo run --quiet

다음과 같이 출력되어야 합니다.

Text: serve reports
Text: preview
Keyword: <>
Status: ✅ ready

두 호출 모두 동일한 &str 매개변수에 전달됩니다. 비어 있는 키워드는 다음 단계에서 사용할 준비된 자리 표시자입니다.

복사하지 않고 부분 문자열 빌리기

이 단계에서는 keyword가 command 안의 serve 접두사만 보도록 만듭니다.

소스 파일을 엽니다.

nano src/main.rs

&text[start..end] 형식의 슬라이스는 범위를 빌립니다. 시작 위치는 포함하고 끝 위치는 포함하지 않습니다. Rust의 문자열 범위는 바이트를 기준으로 계산합니다. serve의 ASCII 문자는 각각 1바이트를 사용하므로 범위는 바이트 0에서 시작하고 바이트 5 앞에서 끝납니다.

Step 2 줄의 빈 리터럴 ""만 &command[0..5]로 바꿉니다.

    let keyword = &command[0..5]; // Step 2: replace the empty slice.

이렇게 만들어진 &str은 command 내부를 가리키며, 두 번째 문자열을 할당하거나 복사하지 않습니다. command가 계속 유효한 범위에 있으므로 keyword를 사용하는 동안 빌린 뷰도 유효합니다.

저장하고 종료한 다음 실행합니다.

cargo run --quiet

이제 마지막 줄이 다음과 같이 출력되어야 합니다.

Keyword: <serve>

소유된 command에는 여전히 전체 텍스트 serve reports가 들어 있고, keyword는 그중 처음 5바이트를 가리키는 빌린 뷰입니다.

UTF-8 문자 경계 지키기

이 단계에서는 정수 인덱싱을 의도적으로 시도하고, 컴파일러의 설명을 확인한 다음, 유효한 UTF-8 슬라이스로 바꿉니다.

UTF-8 문자가 모두 1바이트를 사용하는 것은 아닙니다. "✅ ready"에서 체크 표시 기호는 3바이트를 사용합니다. 따라서 바이트 0이 항상 “첫 번째 문자”를 의미하는 것은 아니며, Rust에서는 String에 status[0]을 사용할 수 없습니다.

파일을 열고 Step 3 출력 줄의 앞에 있는 //만 삭제합니다.

nano src/main.rs

저장하고 종료한 다음 검사를 실행합니다. 여기서는 실패하는 것이 정상입니다.

cargo check

다음과 같은 고정된 메시지를 찾습니다.

error[E0277]: the type `str` cannot be indexed by `{integer}`

이 진단 메시지는 모든 문자가 1바이트 위치를 가진다고 잘못 가정하지 않도록 보호합니다. 파일을 다시 열고 실패하는 출력 줄을 다음 두 줄로 바꿉니다.

    let symbol = &status[0..3];
    println!("Status symbol: {symbol}");

0..3 범위는 3바이트인 기호 바로 뒤에서 끝납니다. 준비된 텍스트와 바이트 경계를 미리 알고 있기 때문에 동작하는 것입니다. 임의의 텍스트에서 사람이 보는 첫 번째 문자를 찾는 일반적인 방법은 아닙니다. UTF-8 문자의 중간을 자르는 범위를 사용하면 런타임에 패닉이 발생하므로, 문자열 범위는 유효한 경계에서 시작하고 끝나야 합니다.

저장하고 종료한 다음 검사와 실행을 수행합니다.

cargo check
cargo run --quiet

전체 출력은 다음과 같아야 합니다.

Text: serve reports
Text: preview
Keyword: <serve>
Status: ✅ ready
Status symbol: ✅

이 출력이 성공적으로 나타나면 최종 슬라이스가 원래 String을 복사하지 않고 완전한 UTF-8 문자 하나를 선택했다는 뜻입니다.

요약

소유된 String 값과 빌린 &str 뷰를 연결하고, 할당 없이 부분 문자열을 선택했습니다. 또한 UTF-8 텍스트를 바이트로 인덱싱하는 문자 배열처럼 다룰 수 없는 이유를 배웠습니다. 안전한 문자열 범위는 유효한 UTF-8 경계에서 시작하고 끝나야 합니다.