代码之家  ›  专栏  ›  技术社区  ›  Addison

为什么vec::len是一种方法?

  •  3
  • Addison  · 技术社区  · 8 年前

    我注意到锈 Vec::len 方法只访问向量的 len 财产。为什么不 伦恩 只是一个公共财产,而不是把一个方法包装起来?我假设这是这样的,如果将来实现发生变化,就不会有任何中断,因为 向量::len 可以更改获取长度的方式,而不使用 Vec 知道。但我不知道还有没有其他原因。

    我的问题的第二部分是关于何时设计api。如果我正在构建自己的api,并且我有一个带有 伦恩 财产,我应该 伦恩 私有和创建公共 len() 方法?在铁锈中公开田地是不是不好?我不这么认为,但我没注意到这件事经常发生。例如,我有以下结构:

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

    如果这些字段中的任何一个是私有的,并且有一个包装器函数,比如 VEC 做?如果是,那为什么?在生锈的情况下,是否有一个很好的指导方针?

    2 回复  |  直到 8 年前
        1
  •  8
  •   Mateen Ulhaq    8 年前

    一个原因是为实现某种长度概念的所有容器提供相同的接口。(例如 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;
        }
    }
    

    将尝试写入超过容器拥有的内存实际结尾的内存。由于这一关键要求,对 伦恩 是被禁止的。


    哲学

    在库代码中使用这样的getter绝对是件好事(疯狂的人可能会试图修改它!).

    然而,我们应该以最小化getter/setter需求的方式设计代码。一个类应该尽可能地独立于它自己的成员。这些行动应该通过各种方式向公众公布。这里我指的是 做有用的事 --不仅仅是一个简单的返回/设置变量的ol getter/setter。尤其是setter可以通过使用构造函数或方法使其成为冗余的。 VEC 向我们展示了其中一些“setter”:

    push
    insert
    pop
    reserve
    ...
    

    因此, VEC 实现提供对外部世界的访问的算法。但它自己管理自己的内脏。

        2
  •  3
  •   Shepmaster Tim Diekmann    8 年前

    这个 Vec 结构外观 有点像 〔1〕 :

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

    我的想法是 ptr 指向已分配的内存块大小 capacity . 如果 VEC 需要比 容量 然后分配新内存。分配内存中未使用的部分未初始化,可能包含任意数据。

    当您调用 VEC 喜欢 push pop ,他们仔细管理 VEC 的内部状态,必要时增加容量,并确保已删除的项被正确删除。

    如果 len 是公共字段,任何具有 VEC ,或者对一个的可变引用,可以设置 伦恩 任何价值。将其设置为高于应有值,您将能够从未初始化的内存中读取,从而导致 未定义的行为 . 把它放低一点,你就可以有效地删除元素而不必正确地删除它们。

    在其他一些编程语言(如javascript)中,数组或向量的api特别允许您通过设置 length 财产。认为一个习惯了这种方法的程序员可能会在生锈的情况下意外地做到这一点,这是不无道理的。

    保持所有字段私有并使用getter方法 len() 允许 VEC 为了保护其内部的易变性,可以提供强大的内存保证,防止用户意外地对自己做坏事。


    [1]实际上,在这个数据结构上建立了抽象层,因此 looks a little different .

    推荐文章