13. Твърде много списъци (live demo), част 1

13. Твърде много списъци (live demo), част 1

13. Твърде много списъци (live demo), част 1

22 ноември 2017

Learning Rust With Entirely Too Many Linked Lists

std::mem::replace

Удобна функция, която ни позволява да разменим две стойности:

use std::mem;

let head = mem::replace(&mut self.head, Link::Empty)

Option::take

По-четим и удобен за използване вариант на предната функция за Option

let head = self.head.take();

// Превежда се отдолу до:
let head = Option::take(&mut self.head);
let head = std::mem::replace(&mut self.head, None);

Трябва self да е взето или като mut self, или &mut self

map & as_ref

Метода map се използва често когато имаме Option:

pub fn peek(&self) -> Option<&T> {
    self.head.as_ref().map(|node| &node.elem)
}

Map обаче взема ownership! Метода as_ref Конвертира Option<T> в Option<&T>, което ни позволява да достъпим вътрешната стойност по reference (ако изобщо има такава).

Итерация по reference

Итерирането по този начин включва вземане на вътрешния &Node изпод Box-а. Това може да изглежда объркващо...

pub fn iter(&self) -> Iter<T> {
    Iter {
        current: self.head.as_ref().map(|node| &**node),
    }
}

Винаги трябва да мислим за типовете! В случая имаме:

Option<Box<Node<T>>> // в списъка.
Option<&Node<T>>     // в итератора. Не искаме Box, защото не искаме ownership

Итерация по reference

// self.head: Option<Box<Node<T>>>
// current:   Option<&Node<T>>
let current = self.head.as_ref().map(|node| &**node);

// node:    &Box<Node<T>>
// *node:   *&Box<Node<T>> -> Box<Node<T>>
// **node:  *Box<Node<T>> -> *&Node<T> -> Node<T>
// &**node: &Node<T>

Алтернативно, функцията Box::as_ref ни дава същия процес с по-малко perl-like код:

let mut current = self.head.as_ref().map(|node| Box::as_ref(node));

// Или, за по-кратко:
let mut current = self.head.as_ref().map(Box::as_ref);

Итерация по mutable reference

let mut current = self.head.as_mut().map(|node| &mut **node);
// Или
let mut current = self.head.as_mut().map(Box::as_mut);

Благодарение на всичките safety check-ове, спокойно можем и да си вземем mutable reference, с почти същия код.

Не е нужно да се ползва map

Тези три имплементации на peek са еквивалентни:

pub fn peek(&self) -> Option<&T> {
    self.head.as_ref().map(|node| &node.elem)
}

pub fn peek(&self) -> Option<&T> {
    match self.head {
        None => None,
        Some(ref node) => Some(&node.elem)
    }
}

pub fn peek(&self) -> Option<&T> {
    let node = self.head.as_ref()?;
    Some(&node.elem)
}

Не е нужно да се ползва map

Дали ще ползвате map, експлицитен pattern-matching, или ? оператора е предимно въпрос на предпочитание. Не всички варианти са използваеми на всички места, разбира се.

Напълно е възможно да започнете с експлицитен pattern-matching, и да видите, че можете да си опростите кода значително с един map. Или да "извадите" стойност от option рано в метода и оттам нататък да работите безопасно с нея.

Експериментирайте, за да откриете с какво се чувствате най-комфортни. Правете го редовно -- силно е вероятно предпочитанията ви да се променят с времето.

Въпроси