Почему Vec :: len является методом? - PullRequest
0 голосов
/ 03 июня 2018

Я заметил, что метод Rust Vec::len просто обращается к свойству len вектора.Почему len не является просто публичным свойством, а не оборачивает метод вокруг него?Я предполагаю, что это так, что в случае, если реализация изменится в будущем, ничто не сломается, потому что Vec::len может изменить способ получения длины без ведома пользователей Vec.Но я не знаю, есть ли другие причины.

Вторая часть моего вопроса касается того, когда я разрабатываю API.Если я создаю свой собственный API и у меня есть структура со свойством len, должен ли я сделать len приватным и создать открытый метод len()?Плохо ли публиковать поля в Rust?Я бы так не думал, но я не замечаю, что это часто делается в Rust.Например, у меня есть следующая структура:

pub struct Segment {
    pub dol_offset: u64,
    pub len: usize,
    pub loading_address: u64,
    pub seg_type: SegmentType,
    pub seg_num: u64,
}

Должно ли какое-либо из этих полей быть закрытым и вместо этого иметь функцию-оболочку, как у Vec?Если так, то почему?Есть ли хорошее руководство для этого в Rust?

Ответы [ 2 ]

0 голосов
/ 03 июня 2018

Структура Vec выглядит примерно так: this [1] :

pub struct Vec<T> {
    ptr: *mut T,
    capacity: usize,
    len: usize,
}

Идея состоит в том, что ptr указывает на выделенный блокпамять размером capacity.Если размер Vec должен быть больше, чем capacity, то выделяется новая память.Неиспользованная часть выделенной памяти неинициализирована и может содержать произвольные данные.

Когда вы вызываете изменяющиеся методы на Vec, например push или pop, они тщательно управляют внутренним состоянием Vec.увеличьте емкость при необходимости и убедитесь, что удаленные элементы правильно отброшены.

Если len было открытым полем, любой код с принадлежащим Vec или изменяемой ссылкой на него может быть установленlen на любое значение.Установите его выше, чем должно быть, и вы сможете читать из неинициализированной памяти, вызывая Неопределенное поведение .Установите его ниже, и вы будете эффективно удалять элементы, не удаляя их должным образом.

В некоторых других языках программирования (например, JavaScript) API для массивов или векторов, в частности, позволяет изменять размер, устанавливая свойство length,Весьма разумно думать, что программист, привыкший к такому подходу, может сделать это случайно в Rust.

Сохранение всех полей закрытыми и использование метода получения для len() позволяет Vec защитить изменчивостьего внутренние компоненты обеспечивают надежную память и предотвращают случайное совершение пользователями плохих действий.


[1] На практике над этой структурой данных построены уровни абстракции, поэтому выглядит немного иначе .

0 голосов
/ 03 июня 2018

Одной из причин является предоставление одного и того же интерфейса для всех контейнеров, которые реализуют некоторое представление о длине.(Например, std::iter::ExactSizeIterator.)

В случае Vec, len() действует как геттер:

impl<T> Vec<T> {
    pub fn len(&self) -> usize {
        self.len
    }
}

Хотя это обеспечивает согласованностьв стандартной библиотеке есть еще одна причина, лежащая в основе этого выбора дизайна ...

Этот геттер защищает от внешней модификации len.Если условие Vec::len <= Vec::buf::cap никогда не выполняется, методы Vec могут пытаться получить доступ к памяти нелегально.Например, реализация Vec::push:

pub fn push(&mut self, value: T) {
    if self.len == self.buf.cap() {
        self.buf.double();
    }
    unsafe {
        let end = self.as_mut_ptr().offset(self.len as isize);
        ptr::write(end, value);
        self.len += 1;
    }
}

попытается выполнить запись в память после фактического конца памяти, принадлежащей контейнеру.Из-за этого критического требования запрещается изменять len.


Philosophy

Определенно хорошо использовать подобный метод получения в библиотечном коде (сумасшедшийлюди могут попытаться изменить его!).

Однако нужно разрабатывать их код таким образом, чтобы минимизировать требования к получателям / установщикам.Класс должен действовать как можно более самостоятельно.Эти действия должны быть доступны для общественности с помощью методов.И здесь я имею в виду методы, которые делают полезные вещи - не просто обычный метод получения / установки, который возвращает / устанавливает переменную.В частности, сеттеры могут быть сокращены за счет использования конструкторов или методов.Vec показывает нам некоторые из этих «сеттеров»:

push
insert
pop
reserve
...

Таким образом, Vec реализует алгоритмы, обеспечивающие доступ к внешнему миру.Но он сам управляет своими внутренностями.

...