Before:为什么要出一期挺莫名其妙的东西?起因还是机考,连续2次机考工程题一分没拿了(太菜了bushi),感觉对C++的各种机制很不熟悉,而Rust又是重要的一门现代编程语言。援引StanfordCS110L 前言的一段话:
如果你学过 C 并接触过一些系统编程的话,应该对 C 的内存泄漏以及指针的危险有所耳闻,但 C 的底层特性以及高效仍然让它在系统级编程中无法被例如 Java 等自带垃圾收集机制的高级语言所替代。而 Rust 的目标则是希望在 C 的高效基础上,弥补其安全不足的缺点。因此 Rust 在设计之初,就有带有很多系统编程的观点。学习 Rust,也能让你之后能用 C 语言编写出更安全更优雅的系统级代码(例如操作系统等)。
Rust Learning _RefCell机制
Rust的所有权机制
Rust的所有权机制要求每个值都有唯一的所有者 (通常是变量),并且在同一时间内只能有一个所有者。所有权的转移可以通过赋值、函数参数传递或返回值来实现。相当于 C++ 中的移动语义(std::move())
在一个值的所有者变量的作用域之外(例如在另外一个函数中)对该值的访问必须通过借用(相当于 C++ 中指向变量的指针)来实现。Rust 中的借用分为两种:
Rust 对变量的借用有着严格的限制:
Rust 的借用机制对编译器优化非常有帮助。由于不可变借用不能与可变借用共存,**被不可变借用指向的值只需要从堆内存中获取一次,之后可以安全地存储在寄存器或栈上缓存中。*相比之下,C++ 中也进行类似的优化,但其他函数修改 const 指针指向的值是未定义行为,可能导致不安全的代码。
Rust 的编译器可以在编译时就能“静态”地检查所有权和借用关系,在运行时无需额外检查。然而,对于堆上对象,在编译期检查所有权和借用关系是非常困难的 。因此,Rust 提供了 RefCell 类型来在运行时检查所有权和借用关系。它有如下方法:
borrow() 与 try_borrow():获取一个不可变借用,返回 Ref 类型。如果当前存在可变借用则失败。borrow() 会 panic,相当于 C++ 中的 abort,而 try_borrow() 返回一个 Result<Ref, BorrowError>,相当于 C++ 中的 std::optional<Ref>
borrow_mut() 与 try_borrow_mut():获取一个可变借用,返回 RefMut 类型。如果当前存在任何借用则会失败
返回的 Ref 和 RefMut 包装器实现了解引用操作符,可以像使用普通引用一样使用
当 Ref 和 RefMut 的生命周期结束时,会自动减少或重置借用计数
当 RefCell 的生命周期结束时,若仍有借用存在,则会 panic
C++中的std::optional
编程中,我们经常会需要表示或处理一个“可能为空”的变量,可能是一个为包含任何元素的容器,可能是一个类型的指针没有指向任何有效的对象实例,再或者是一个对象没有被赋予有效的值。
C++17中的std::optional为解决这类问题提供了简单的解决方案。optional可以看作是T类型变脸与一个布尔值的打包。其中的布尔值用来表示T是否为“空”。
std::optional可以:包含一个类型为T的值或者不包含任何值(处于"空"状态)
不包含任何值显示表示为:std::nullopt
Advantage: 明确表示值可能存在或不存在;强制使用者考虑值缺失的情况;通常比使用指针或额外标志更高效
C++中的const 成员函数
在C++中,只有被声明为const的成员函数才能被一个const类对象调用。
const 成员函数不能修改类的普通成员变量
如果想修改,需加上mutable关键字,允许 const 成员函数修改内部计数器(确实这个关键字常见于计数器)
剩余代码的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 #include <iostream> #include <optional> #include <stdexcept> class RefCellError : public std::runtime_error {public : explicit RefCellError (const std::string& message) : std::runtime_error(message) { } virtual ~RefCellError () = default ; };class BorrowError : public RefCellError {public : explicit BorrowError (const std::string& message) : RefCellError(message) { } };class BorrowMutError : public RefCellError {public : explicit BorrowMutError (const std::string& message) : RefCellError(message) { } };class DestructionError : public RefCellError {public : explicit DestructionError (const std::string& message) : RefCellError(message) { } };template <typename T>class RefCell {private : T value; mutable size_t borrow_num; mutable size_t borrow_mut_num;public : class Ref ; class RefMut ; explicit RefCell (const T& initial_value) :value(initial_value),borrow_mut_num(0 ),borrow_num(0 ) { } explicit RefCell (T && initial_value) :value(std::move(initial_value)),borrow_mut_num(0 ),borrow_num(0 ) { } RefCell (const RefCell&) = delete ; RefCell& operator =(const RefCell&) = delete ; RefCell (RefCell&&) = delete ; RefCell& operator =(RefCell&&) = delete ; Ref borrow () const { if (borrow_mut_num != 0 ) { throw BorrowError ("RuntimeError" ); } ++ borrow_num; return Ref (*this ); } std::optional<Ref> try_borrow () const { if (borrow_mut_num != 0 ) { return std::nullopt ; } ++ borrow_num; return Ref (*this ); } RefMut borrow_mut () { if (borrow_num != 0 ) { throw BorrowMutError ("RuntimeError" ); } if (borrow_mut_num != 0 ) { throw BorrowMutError ("RuntimeError" ); } ++ borrow_mut_num; return RefMut (*this ); } std::optional<RefMut> try_borrow_mut () { if (borrow_num != 0 ) { return std::nullopt ; } if (borrow_mut_num != 0 ) { return std::nullopt ; } ++ borrow_mut_num; return RefMut (*this ); } class Ref { private : const RefCell* refcell; bool valid = true ; public : Ref (const RefCell &c):refcell (&c),valid (true ) {} ~Ref () { if (valid ) { -- refcell->borrow_num; } } const T& operator *() const { if (!valid || refcell->borrow_num == 0 ) { throw BorrowError ("RuntimeError" ); } return refcell->value; } const T* operator ->() const { if (!valid || refcell->borrow_num == 0 ) { throw BorrowError ("RuntimeError" ); } return &refcell->value; } Ref (const Ref& other):refcell (other.refcell),valid (other.valid) { if (valid){ ++ refcell->borrow_num; } } Ref& operator =(const Ref& other) { if (this != &other) { if (valid) { -- refcell->borrow_num; } refcell = other.refcell; valid = other.valid; if (valid){ ++ refcell->borrow_num; } } return *this ; } Ref (Ref&& other) noexcept :refcell (other.refcell),valid (other.valid) { other.valid = false ; } Ref& operator =(Ref&& other) { if (this != &other) { if (valid) { --refcell->borrow_num; } refcell = other.refcell; valid = true ; other.valid = false ; } return *this ; } }; class RefMut { private : RefCell* refcell; bool valid = true ; public : RefMut (RefCell &c):refcell (&c),valid (true ) {} ~RefMut () { if (valid ) { -- refcell->borrow_mut_num; } } T& operator *() { if (!valid || refcell->borrow_mut_num == 0 ) { throw BorrowMutError ("RuntimeError" ); } return refcell->value; } T* operator ->() { if (!valid || refcell->borrow_mut_num == 0 ) { throw BorrowMutError ("RuntimeError" ); } return &refcell->value; } RefMut (const RefMut&) = delete ; RefMut& operator =(const RefMut&) = delete ; RefMut (RefMut&& other) noexcept :refcell (other.refcell),valid (other.valid) { other.valid = false ; } RefMut& operator =(RefMut&& other) { if (this != &other) { if (valid) { --refcell->borrow_mut_num; } refcell = other.refcell; valid = other.valid; other.valid = false ; } return *this ; } }; ~RefCell () { if (borrow_mut_num > 0 || borrow_num > 0 ) { throw DestructionError ("RuntimeError" ); } borrow_mut_num = 0 ; borrow_num = 0 ; } };
Reference