
Size: a a a
impl VaultKeySet {
fn get_label(&self) -> String {
match self.key_data.label() {
Some(label) if !label.is_empty() => label.into(),
_ => format!("{}{}", KEY_LEGACY_PREFIX, self.legacy_number),
}
}
}
label
отсутствует, то последующий код не исполняется вовсе. И никаких причудливых танцев с .has_value()
и оператором разыменования с неопределённым поведением.На этом месте была статья, которая больно задела чувства большого числа наших покупателей, сотрудников, партнеров и поставщиков.
Мы сожалеем, что так получилось, и считаем эту публикацию своей ошибкой, ставшей проявлением непрофессионализма отдельных сотрудников.
Ожидаемо, на него обрушился шквал критики с противоположной стороны — на этот раз за то, что прогнулись под требования гомофобов (подавляющее большинство которых даже не были покупателями магазина). При этом надо отметить, что даже с сугубо с точки зрения бизнеса этот манёвр был проигрышным: Вкуссвилл потерял заметную долю лояльной аудитории и при этом не приобрёл новой.read_to_string
, но забывают очищать строку в конце итерации, из-за чего в строке остаётся то, что было прочитано на предыдущих операциях. Более того, просто воткнуть s.clear();
в конце тела может быть недостаточно: если в цикле есть оператор continue
, то до исполнения этой строчки дело может и не дойти — в подобных случаях не только новички делают ошибки. Было бы хорошо иметь что-то, что выполняется автоматически в конце каждой итерации цикла вне зависимости то того, как она выполняется... И такая вещь в Rust есть: Drop::drop
aka деструктор!struct AutoDo<T, F> {
value: T,
action: F,
}
struct DoOnDrop<'a, T, F>(&'a mut AutoDo<T, F>);
F
— которое неизбежно появится для описания того факта, что это функция — повторялось при определении структуры. Без выполнения этого требования компилятор выдаст ошибку. А как, собственно говоря, должно выглядеть это требование? Это должна быть функция, которая принимает мутабельную ссылку на значение, причём не с каким-то конкретным временем жизни, а с произвольным. В итоге определение выглядит так:struct DoOnDrop<'a, T, F: FnMut(&mut T)>(&'a mut AutoDo<T, F>);
struct DoOnDrop<'a, T, F: for<'val> FnMut(&'val mut T)>(&'a mut AutoDo<T, F>);
impl<'a, T, F: FnMut(&mut T)> Drop for DoOnDrop<'a, T, F> {
fn drop(&mut self) {
(self.0.action)(&mut self.0.value)
}
}
self.0.action
тут необходимы, поскольку иначе это будет проинтепретировано, как вызов метода AutoDo::action
, коего, разумеется нет.DoOnDrop
трейты Deref{, Mut}<Target = T>
, а также метод AutoDo::get
, который будет оборачивать ссылку в DoOnDrop
. Реализацию, с вашего позволения, опущу в силу тривиальности.nope
— их она будет просто отбрасывать:fn main() {
use std::io::Read;
let stdin = std::io::stdin();
let mut stdin = stdin.lock();
let mut buf = AutoDo::new(String::new(), String::clear);
loop {
let mut buf = buf.get();
if let Ok(0) | Err(_) = stdin.read_to_string(&mut buf) {
break;
}
if buf.contains("nope") {
continue; // работает и с continue
}
println!("{}", buf.to_uppercase());
}
let buf = buf.into_inner();
assert!(buf.is_empty()); // буфер в итоге пустой
}
std::variant
— могли бы сделать нормальный паттерн-матчинг, а получилось какое-то дикое говно, которое невозможно читать, а написать корректно ещё сложнее. И ещё пачку новых способов инициализации, куда же без этого.