[원본 글]
String
을 인수로 받는 함수fn print_me(msg: String) {
println!("the message is {}", msg);
}
fn main() {
let msg = "hello world";
print_me(msg);
}
error[E0308]: mismatched types
print_me(msg);
^^^ expected struct `String`, found `&str
let msg = "hello world".to_string()
으로 변환하면 문제 없으나, clone()
을 수행하는 것과 유사print_me()
에서 인수를 String
보다 &str
사용하는 것이 더 나은 이유String
타입은 &str
타입으로 Deref를 통해 변환이 용이함fn print_me(msg: &str) { println!("msg = {}", msg); }
fn main() {
let string = "hello world";
print_me(string);
let owned_string = "hello world".to_string();
// or String::from_str("hello world")
print_me(&owned_string);
let counted_string = std::rc::Rc::new("hello world".to_string());
print_me(&counted_string);
let atomically_counted_string = std::sync::Arc::new("hello world".to_string());
print_me(&atomically_counted_string);
}
print_me()
는 &str
을 인수로 받음owned_string
은 &String
에서 자동으로 &str
로 변환됨Rc
와 Arc
에서도 마찬가지로 자동 변환됨.to_string()
으로 어질러지는 것을 피할 수 있음struct
struct Person {
name: &str,
}
fn main() {
let _person = Person { name: "Herman" };
}
error[E0106]: missing lifetime specifier
name: &str,
^ expected named lifetime parameter
name
이 Person
보다 오래 남아있지 않도록 방지struct Person {
name: &'a str,
}
fn main() {
let _person = Person { name: "Herman" };
}
error[E0261]: use of undeclared lifetime name `'a`
name: &'a str,
^^ undeclared lifetime
name
보다 Person
이 더 오래 남아있도록 Person
에도 힌트 제공 필요struct Person<'a> {
name: &'a str,
}
fn main() {
let _person = Person { name: "Herman" };
}
greet()
함수 구현struct Person<'a> {
name: &'a str,
}
impl Person {
fn greet(&self) {
println!("Hello, my name is {}", self.name);
}
}
fn main() {
let person = Person { name: "Herman" };
person.greet();
}
error[E0726]: implicit elided lifetime not allowed here
impl Person {
^^^^^^ expected lifetime parameter
impl
에서도 lifetime 힌트 제공 필요impl Person<'a>
해도 다음 에러 발생error[E0261]: use of undeclared lifetime name `'a`
impl Person<'a> {
^^ undeclared lifetime
impl<'a> Person<'a>
로 해결struct Person<'a> {
name: &'a str,
}
impl<'a> Person<'a> {
fn greet(&self) {
println!("Hello, my name is {}", self.name);
}
}
fn main() {
let person = Person { name: "Herman" };
person.greet();
}
struct
내에서의 String
과 &str
struct 내에서 String
을 사용하는게 나을지, &str
을 사용하는게 나을지?
변수의 ownership을 가질 필요가 없으면 reference 사용
struct 밖에서 변수를 사용할 필요가 있는가?
struct Person {
name: String,
}
impl Person {
fn greet(&self) {
println!("Hello, my name is {}", self.name);
}
}
fn main() {
let name = "Herman".to_string();
let person = Person { name: name };
person.greet();
println!("My name is {}", name); // move error 발생
}
데이터 타입이 큰가? 불필요한 메모리 복사 발생 여부
'static
에 대한 고려struct Person {
name: &'static str,
}
impl Person {
fn greet(&self) {
println!("Hello, my name is {}", self.name);
}
}
fn main() {
let person = Person { name: "Herman" };
person.greet();
}
&str
보다 String
이 더 나은 경우 대처name
변수를 소유하는게 반드시 필요하다고 가정.to_string()
으로 변환 필요struct Person {
name: String,
}
impl Person {
fn new(name: &str) -> Person {
Person {
name: name.to_string(),
}
}
}
fn main() {
let name = "Herman".to_string();
let person = Person::new(name.as_ref());
}
into trait
&str
을 자동으로 String
으로 변환&str
과 String
모두 받을 수 있음struct Person {
name: String,
}
impl Person {
fn new<S: Into<String>>(name: S) -> Person {
Person { name: name.into() }
}
}
// 위와 같은 구현
// impl Person {
// fn new<S>(name: S) -> Person where S: Into<String> {
// Person { name: name.into() }
// }
// }
fn main() {
let person = Person::new("Herman");
let person = Person::new("Herman".to_string());
}
&str
또는 String
의 함수 반환fn remove_spaces(input: &str) -> String {
let mut buf = String::with_capacity(input.len());
for c in input.chars() {
if c != ' ' {
buf.push(c);
}
}
buf
}
&str
타입이고 반환값은 String
이어서 다른데?String
으로 하여 타입을 맞춰주면?fn remove_spaces(input: String) -> String { ... }
&str
이면 String
으로 변환하는 과정을 한번 더 거쳐야 함Cow
타입의 lifetime은 &str
과 같음use std::borrow::Cow;
fn remove_spaces<'a>(input: &'a str) -> Cow<'a, str> {
if input.contains(' ') {
let mut buf = String::with_capacity(input.len());
for c in input.chars() {
if c != ' ' {
buf.push(c);
}
}
return Cow::Owned(buf);
}
return Cow::Borrowed(input);
}
let s = remove_spaces("Herman"); // s는 Cow::Borrowed
let len = s.len(); // s를 변경시키지 않는 immutable 함수 호출
// Deref를 통해 자동 변환됨
let owned: String = s.into_owned(); // 새로운 문자열을 위해 메모리 할당
let s = remove_spaces("Herman Radtke"); // s는 Cow::Owned
let len = s.len(); // s를 변경시키지 않는 immutable 함수 호출
// Deref를 통해 자동 변환됨
let owned: String = s.into_owned(); // 이미 String이어서 새로운 메모리 할당 없음
Into
trait 활용fn remove_spaces<'a>(input: &'a str) -> Cow<'a, str> {
if input.contains(' ') {
let mut buf = String::with_capacity(input.len());
let v: Vec<char> = input.chars().collect();
for c in v {
if c != ' ' {
buf.push(c);
}
}
return buf.into(); // .into()
}
return input.into(); // .into()
}
fn remove_spaces<'a>(input: &'a str) -> Cow<'a, str> {
if input.contains(' ') {
input
.chars()
.filter(|&x| x != ' ')
.collect::<std::string::String>()
.into()
} else {
input.into()
}
}
String::with_capacity()
를 사용하는 이유buf
를 위해 String::new()
를 사용하지 않고 String::with_capacity()
사용String
은 실제로는 Vec
String::new()
하면 0바이트 벡터를 생성하고, 내용물이 추가될 때 벡터의 용량을 늘리기 위해 메모리 재할당with_capacity()
를 사용하는 것을 권장