TL; DR: вместо того, чтобы можно использовать &str
, &[T]
или &T
для обеспечения более общего кода.
Одна из основных причин использования a String
или a Vec
заключается в том, что они позволяют увеличивать или уменьшать емкость. Однако, если вы принимаете неизменяемую ссылку, вы не можете использовать какие-либо из этих интересных методов в Vec
или String
.
Прием &String
, &Vec
или &Box
же требует аргумент , чтобы быть выделена в куче , прежде чем вы можете вызвать функцию. Принятие a &str
позволяет строковый литерал (сохраненный в данных программы), а принятие &[T]
или &T
разрешает массив или переменную, распределенную в стеке. Ненужное выделение - это потеря производительности. Обычно это проявляется сразу, когда вы пытаетесь вызвать эти методы в тесте или main
методе:
awesome_greeting(&String::from("Anna"));
total_price(&vec![42, 13, 1337])
is_even(&Box::new(42))
Другое соображение производительности заключается в том &String
, что &Vec
и &Box
введение ненужного уровня косвенного обращения, поскольку вам нужно разыменовать &String
объект, чтобы получить, String
а затем выполнить второе разыменование, чтобы в конечном итоге &str
.
Вместо этого вы должны принять строку slice ( &str
), slice ( &[T]
) или просто ссылку ( &T
). A &String
, &Vec<T>
или &Box<T>
будет автоматически принужден к a &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
может вызываться со ссылкой на array ( &[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);
}
Специально для срезов вы также можете принять &mut [T]
или &mut str
. Это позволяет вам изменять определенное значение внутри среза, но вы не можете изменить количество элементов внутри среза (что означает, что это очень ограничено для строк):
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();
}
}
&str
является более общим (например, накладывает меньше ограничений) без ограниченных возможностей»? Также: я думаю, пункт 3 часто не так важен. ОбычноVec
s иString
s будут жить в стеке и часто даже где-то рядом с текущим кадром стека. Стек обычно горячий, и разыменование будет выполняться из кеша процессора.total_price(&prices[0..4])
не требует выделения нового вектора для среза.&str
и почему (исходящий из Python, поэтому я обычно не имею дела с типами явно). ВсёВ дополнение к ответу Shepmaster в , еще одна причина , чтобы принять
&str
(а так же и&[T]
т.д.) из - за всех других типов , кромеString
и&str
которые также удовлетворяютDeref<Target = str>
. Один из наиболее ярких примеров - это тоCow<str>
, что позволяет вам очень гибко выбирать, имеете ли вы дело с собственными или заимствованными данными.Если у вас есть:
Но вам нужно вызвать это с помощью
Cow<str>
, вам нужно будет сделать это:Когда вы меняете тип аргумента на
&str
, вы можете использовать егоCow
без лишних затрат, как и в случае сString
:Принятие
&str
делает вызов вашей функции более единообразным и удобным, а «самый простой» способ теперь также является наиболее эффективным. Эти примеры также будут работать сCow<[T]>
и т. Д.источник