Почему подчеркивание префиксных переменных существует?
Я изучаю Rust и столкнулся с тем фактом, что добавление подчеркивания в начале имени переменной заставит компилятор не предупреждать, если она не используется. Мне интересно, почему эта функция существует, так как неиспользуемые переменные осуждаются.
5 ответов
Я вижу несколько причин:
- Вы вызываете функцию, которая возвращает
#[must_use]
типа, но в вашем конкретном случае вы знаете, что можете смело игнорировать значение. Можно использовать_
шаблон для этого (который не является привязкой к переменной, это собственный шаблон, но, вероятно, именно отсюда и происходит соглашение о префиксах подчеркивания), но вы можете задокументировать, почему вы игнорируете значение или что это за значение. Это особенно часто встречается в тестах по моему опыту. - Макросы. Переменная, созданная в макросе, может или не может использоваться позже. Было бы неприятно не иметь возможности отключить предупреждения при вызове макроса. В этом случае есть соглашение об удвоении символов подчеркивания, например, Clippy
used_underscore_binding
ворс. - RAII. Вы могли бы хотеть иметь переменную, существующую для ее побочного эффекта деструктора, но не использовать ее иначе. Не возможно просто использовать
_
для этого варианта использования, как_
не является привязкой переменной, и значение будет отброшено в конце оператора.
Вот несколько примеров того, почему вы можете захотеть игнорировать неиспользуемую переменную. Рассматривать _s
в следующей функции.
fn add_numbers(f: i32, _s: i32) -> i32 {
f + 1
}
_s
Переменная делает это таким образом, чтобы мы могли сохранить сигнатуру, даже если мы ее не реализовали. Это также работает, если мы узнали, что нам не нужно _s
но поскольку наша библиотека используется во многих различных проектах, мы не хотели менять API для нашей функции. Это может или не может быть плохой практикой, но может быть полезно в ситуации, когда _s
Нужно остаться и ничего не делать. Мы могли бы также использовать _
здесь, но _s
потенциально имеет больше значения относительно того, для чего переменная в будущем.
Следующее место, где это может быть полезно, это когда тип реализует Drop
и тебя волнует, где происходит эта логика. В этом примере вы можете увидеть, что _result
переменная нужна, чтобы Drop
бывает в конце.
fn main() {
let mut num = 1;
// let _ = try_add_numbers(&mut num); // Drop is called here for _
let _result = try_add_numbers(&mut num); // without the _result we have a warning.
println!("{}", num);
// Drop is called here for the _result
}
// keep the api the same even if an aurgument isn't needed anymore or
// has not been used yet.
fn add_numbers(f: i32, _s: i32) -> i32 {
f + 1
}
// This function returns a result
fn try_add_numbers(i: &mut i32) -> Result<GoodResult, GoodResult> {
if *i > 3 {
return Err(GoodResult(false));
}
*i = add_numbers(*i, 0);
Ok(GoodResult(true))
}
struct GoodResult(bool);
impl Drop for GoodResult {
fn drop(&mut self) {
let &mut GoodResult(result) = self;
if result {
println!("It worked");
} else {
println!("It failed");
}
}
}
Если мы используем let _result = try_add_numbers(&mut num);
у нас есть переменная, которая находится в области видимости до тех пор, пока не будет вызван конец main и drop. Если бы мы использовали let _ = try_add_numbers(&mut num);
мы все еще не получаем предупреждение, но в конце оператора вызывается drop. Если мы используем try_add_numbers(&mut num);
без привязки let мы получаем предупреждение. Вывод этой программы меняется в зависимости от того, что мы используем с нашей функцией try_add_numbers.
It worked
2
или же
2
It worked
Таким образом, есть применение для обоих _
а также _named
переменные, которые нужно выбирать в зависимости от того, какими должны быть выходные данные ваших программ. Поиграйте с моим примером на игровой площадке, чтобы почувствовать это.
- является привязкой значения, и пространство стека будет выделено для хранения его значения.
-
let _a
что-то ведет себя какlet a
. Кроме того, он отмечен какintentional
, чтобы компилятор не выдавал предупреждение, если_a
не используется. -
let _
является шаблоном, и_
этоreserved identifier
которые нельзя использовать в другом месте. Это не приведет к выделению пространства стека, поэтому значение в правой части=
будет выпущен вскоре после этого заявления.
Вот пример: Детская площадка
pub struct Node {
value: usize,
}
impl Drop for Node {
fn drop(&mut self) {
println!("drop() {}", self.value);
}
}
pub fn square() {
let a = Node { value: 1 };
let _a = Node { value: 2 };
let _ = Node { value: 3 };
println!("Hello, world!");
}
fn main() {
square();
}
вывод:
drop() 3
Hello, world!
drop() 2
drop() 1
Вы можете прочитать это , чтобы узнать больше
Я наткнулся здесь через Google, когда искал это предупреждение, связанное с переменными соответствия. Это косвенно связано.
Иногда у вас может быть код, в котором вы получаете Result
и хотите сопоставить случаи, но вас не волнует значение ошибки. Вместо того, чтобы использовать_e
или что-то в этом роде, вы можете просто использовать _
который явно не связывает. Вот конкретный пример. Нас не волнует значение ошибки, поскольку мы возвращаем свою собственную.
fn some_method() -> Result<u32, MyCustomError> {
// ...
let id: u32 = match some_str.parse() {
Ok(value) => value,
Err(_) => return Err(MyCustomError::Blah)
};
// ...
}
Просто определите тип переменной, т.е.
например:-
fn names(){ //this case the function gives an warning like "help: if this is intentional, prefix it with an underscore: `_myname`"
let myname = "yash";
}
поэтому с помощью последних изменений в вашем коде вы можете устранить эту ошибку: -
fn name(){ //here warning won't come
let myname: &str = "bash"
}