빌림 오류 고치기 예제 사다리 문서의 문제 3 풀이 코드다.
//! 빌림 오류 고치기 예제 사다리 문제 3: 함수 안에서 만든 String을 가리키는 &str을 돌려주면 E0515.
//! 소유한 String을 돌려주거나, 입력에서 바로 잘라 돌려주면 고쳐진다.
//! 의존성: 없음 (표준 라이브러리만)
//! 실행: python3 "_시스템/Rust 계획/rs_check.py" "Rust/4.연습문제/09_borrow-errors-ladder_p3.rs"
use std::process::Command;
/// 코드 조각을 rustc로 컴파일해 stderr를 돌려준다.
fn compile_errors(code: &str) -> String {
let dir = std::env::temp_dir().join(format!("vrL3_{}_{}", std::process::id(), code.len()));
std::fs::create_dir_all(&dir).unwrap();
let file = dir.join("snippet.rs");
std::fs::write(&file, code).unwrap();
let out = Command::new("rustc")
.args([
"--edition",
"2024",
"--crate-type=lib",
"--emit=metadata",
"--out-dir",
])
.arg(&dir)
.arg(&file)
.output()
.expect("rustc 실행");
let _ = std::fs::remove_dir_all(&dir);
String::from_utf8_lossy(&out.stderr).into_owned()
}
/// 오류 코드와, 문서에 옮긴 메시지 조각(본문·라벨·도움말)이 모두 나오는지 확인한다.
fn expect_error(code: &str, err: &str, phrases: &[&str]) {
let stderr = compile_errors(code);
assert!(
stderr.contains(&format!("error[{err}]")),
"{err} 기대, 실제:\n{stderr}"
);
for phrase in phrases {
assert!(stderr.contains(phrase), "'{phrase}' 기대, 실제:\n{stderr}");
println!("{err} 확인: {phrase}");
}
}
fn expect_ok(code: &str) {
let stderr = compile_errors(code);
assert!(
!stderr.contains("error"),
"컴파일 성공 기대, 실제:\n{stderr}"
);
}
const BROKEN: &str = r#"pub fn longest_line_upper(text: &str) -> &str {
let upper = text.to_uppercase();
upper.lines().max_by_key(|l| l.len()).unwrap_or("")
}"#;
/// 고친 버전 1: 새로 만든 글자는 소유한 String으로 돌려준다.
fn longest_line_upper(text: &str) -> String {
text.lines()
.max_by_key(|l| l.len())
.unwrap_or("")
.to_uppercase()
}
/// 고친 버전 2: 대문자로 바꿀 필요가 없다면 입력에서 잘라 돌려준다(수명 생략 규칙 2).
fn longest_line(text: &str) -> &str {
text.lines().max_by_key(|l| l.len()).unwrap_or("")
}
fn main() {
expect_error(
BROKEN,
"E0515",
&[
"cannot return value referencing local variable `upper`",
"returns a value referencing data owned by the current function",
"`upper` is borrowed here",
],
);
expect_ok(
r#"pub fn longest_line_upper(text: &str) -> String { text.lines().max_by_key(|l| l.len()).unwrap_or("").to_uppercase() }"#,
);
let text = "ab\nlonger line\nmid";
assert_eq!(longest_line_upper(text), "LONGER LINE");
assert_eq!(longest_line(text), "longer line");
assert_eq!(longest_line(""), "");
// 돌려받은 슬라이스는 입력 버퍼 안을 가리킨다
let at = longest_line(text).as_ptr() as usize - text.as_ptr() as usize;
assert_eq!(at, 3);
println!("ALL CHECKS PASSED");
}