String (& String), Vec (& Vec) 또는 Box (& Box)에 대한 참조를 함수 인수로 받아들이지 않는 이유는 무엇입니까?
&String
인수로 사용하는 Rust 코드를 작성했습니다 .
fn awesome_greeting(name: &String) {
println!("Wow, you are awesome, {}!", name);
}
나는 또한에 대한 참조를 취 코드를 작성했습니다 Vec
또는 Box
:
fn total_price(prices: &Vec<i32>) -> i32 {
prices.iter().sum()
}
fn is_even(value: &Box<i32>) -> bool {
**value % 2 == 0
}
그러나 이렇게하는 것은 좋은 생각이 아니라는 피드백을 받았습니다. 왜 안돼?
TL; DR : 하나 대신 사용할 수 있습니다 &str
, &[T]
또는 &T
보다 일반적인 코드를 허용 할 수 있습니다.
a
String
또는 a 를 사용하는 주된 이유 중 하나Vec
는 용량을 늘리거나 줄일 수 있기 때문입니다. 그러나 변경 불가능한 참조를 수락하면Vec
또는 에서 흥미로운 메서드를 사용할 수 없습니다String
.을 수락
&String
,&Vec
또는&Box
도 것은 필요 당신이 함수를 호출하기 전에 힙에 할당되는 인수. a를&str
허용하면 문자열 리터럴 (프로그램 데이터에 저장 됨)을 허용하고&[T]
or를&T
허용하면 스택 할당 배열 또는 변수를 허용합니다. 불필요한 할당은 성능 손실입니다. 이는 일반적으로 테스트 또는 메서드에서 이러한 메서드를 호출하려고 할 때 즉시 노출됩니다main
.awesome_greeting(&String::from("Anna"));
total_price(&vec![42, 13, 1337])
is_even(&Box::new(42))
또 다른 성능 고려 사항은이다
&String
,&Vec
그리고&Box
당신이를 역 참조 할 필요가 같은 간접 불필요한 레이어를 소개&String
를 얻기 위해String
다음에서 결국에 두 번째 역 참조를 수행합니다&str
.
대신 문자열 슬라이스 ( &str
), 슬라이스 ( &[T]
) 또는 참조 ( &T
) 만 허용해야합니다 . (A)는 &String
, &Vec<T>
또는 &Box<T>
자동으로 강제 변환됩니다 &str
, &[T]
또는 &T
각각.
fn awesome_greeting(name: &str) {
println!("Wow, you are awesome, {}!", name);
}
fn total_price(prices: &[i32]) -> i32 {
prices.iter().sum()
}
fn is_even(value: &i32) -> bool {
*value % 2 == 0
}
이제 더 광범위한 유형 세트로 이러한 메소드를 호출 할 수 있습니다. 예를 들어 awesome_greeting
는 문자열 리터럴 ( "Anna"
) 또는 할당 된 String
. total_price
배열 ( &[1, 2, 3]
)에 대한 참조 또는 할당 된 Vec
.
추가하거나에서 항목을 제거하려는 경우 String
또는 Vec<T>
, 당신은 걸릴 수 변경 가능한 참조 ( &mut String
또는 &mut Vec<T>
) :
fn add_greeting_target(greeting: &mut String) {
greeting.push_str("world!");
}
fn add_candy_prices(prices: &mut Vec<i32>) {
prices.push(5);
prices.push(25);
}
Specifically for slices, you can also accept a &mut [T]
or &mut str
. This allows you to mutate a specific value inside the slice, but you cannot change the number of items inside the slice (which means it's very restricted for strings):
fn reset_first_price(prices: &mut [i32]) {
prices[0] = 0;
}
fn lowercase_first_ascii_character(s: &mut str) {
if let Some(f) = s.get_mut(0..1) {
f.make_ascii_lowercase();
}
}
In addition to Shepmaster's answer, another reason to accept a &str
(and similarly &[T]
etc) is because of all of the other types besides String
and &str
that also satisfy Deref<Target = str>
. One of the most notable examples is Cow<str>
, which lets you be very flexible about whether you are dealing with owned or borrowed data.
If you have:
fn awesome_greeting(name: &String) {
println!("Wow, you are awesome, {}!", name);
}
But you need to call it with a Cow<str>
, you'll have to do this:
let c: Cow<str> = Cow::from("hello");
// Allocate an owned String from a str reference and then makes a reference to it anyway!
awesome_greeting(&c.to_string());
When you change the argument type to &str
, you can use Cow
seamlessly, without any unnecessary allocation, just like with String
:
let c: Cow<str> = Cow::from("hello");
// Just pass the same reference along
awesome_greeting(&c);
let c: Cow<str> = Cow::from(String::from("hello"));
// Pass a reference to the owned string that you already have
awesome_greeting(&c);
Accepting &str
makes calling your function more uniform and convenient, and the "easiest" way is now also the most efficient. These examples will also work with Cow<[T]>
etc.
'program story' 카테고리의 다른 글
grunt-contrib-copy에서“확장”옵션은 무엇을합니까? (0) | 2020.08.22 |
---|---|
Java와 C / C ++ 간의 프로세스 간 통신을위한 가장 빠른 (낮은 지연) 방법 (0) | 2020.08.22 |
Node.js에서 여러 콜백을 기다리는 관용적 방법 (0) | 2020.08.22 |
이미지를 ASCII 아트로 변환 (0) | 2020.08.22 |
내가 할 수 없어야하는데 왜 TypeScript 개인 멤버에 액세스 할 수 있습니까? (0) | 2020.08.22 |