Python数据类型

collections

array

headq

bisect

weakref

datetime

calender

types

copy

pprint

reprlib

enum

数据模型--基本数据类型

对象、值、类型

对象:python中对数据的抽象

  • python中所有数据都是由对象、对象间关系表示

    • 按冯诺依曼“存储程序计算机”,代码本身也是由对象表示

编号、类型、值

每个对象都有各自编号类型

  • 编号:可以视为对象在内存中地址,对象创建后不变

    • id()函数:获取代表对象编号的整形
    • is算符:比较对象编号判断是否为同一对象
  • 类型:决定对象支持的操作、可能取值

    • 类型会影响对象行为几乎所有方面,甚至对象编号重要性 也受到影响,如:对于会得到新值的运算
      • 不可变类型:可能返回同类型、同取值现有对象引用
        • a = b = 1ab可能指向相同对象1 (取决于具体实现)
      • 可变类型:不允许返回已存在对象
        • c=[];d=[]:会保证cd指向不同、单独 空列表(c=d=[]将同一对象赋给cd
    • 对象创建后保持不变
    • type:返回对象类型
    • CPython:相同整形值都引用同一个对象
  • 值:通过一些特征行为表征的抽象概念

    • 对象值在python中是抽象概念

      • 对象值没有规范的访问方法
      • 不要求具有特定的构建方式,如:值由其全部数据 属性组成
    • 对象值可变性由其类型决定

      • 可变的:值可以改变的对象
      • 不可变的:值(直接包含对象编号)不可改变的对象
    • 比较运算符实现了特定对象值概念,可以认为是 通过实现对象比较间接定义对象值
  • CPython:id(x)返回存放x的地址

对象销毁

对象不会被显式销毁(del仅是移除名称绑定)

  • 无法访问时可能被作为垃圾回收

    • 允许具体实现推迟垃圾回收或完全省略此机制
    • 实现垃圾回收是质量问题,只要可访问对象不会被回收 即可
    • 不要依赖不可访问对象的立即终结机制,应当总是显式 关闭外部资源引用
  • 以下情况下,正常应该被回收的对象可能继续存活

    • 使用实现的跟踪、调试功能
    • 通过try...except...语句捕捉异常
  • CPython:使用带有(可选)延迟检测循环链接垃圾的 引用计数方案
    • 对象不可访问时立即回收其中大部分,但不保证 回收包含循环引用的垃圾

标准类型层级结构

  • 以下是python内置类型的列表,扩展模块可以定义更多类型
  • 以下有些类型有特殊属性,这些特殊属性不应用作通常使用, 其定义在未来可能改变

None

NoneType:只有一种取值,None是具有此值的唯一对象

  • 通过内置名称None访问
  • 多数情况表示空值,如
    • 未显式指明返回值函数返回None
  • 逻辑值:假

NotImplemented

NotImplementedType:只有一种取值,NotImplemented是具有 此值的唯一对象

  • 通过内置名称NotImplemented访问
  • 数值、富比较方法在操作数没有该实现操作时应返回此值
    • 返回NotImplemented前,解释器会依据运算符尝试反射 方法、委托回退方法
  • 逻辑值:真

Ellipsis

ellipsis:只有一种取值,Ellipsis是具有此值的唯一对象

  • 通过字面值...、内置名称Ellipsis访问
  • 逻辑值:真

numbers.Number

number.Number:由数字字面值创建,被作为算法运算符、算数 内置函数返回结果

  • 不可变:一旦创建其值不再改变
  • 类似数学中数字,但也受限于计算机对数字的表示方法

numbers.Integral

numbers.Integral:表示数学中整数集合

  • int:整形,表示任意大小数字,仅受限于可用内存

    • 变换、掩码运算中以二进制表示
    • 负数以2的补码表示(类似符号位向左延伸补满空位)
  • bool:布尔型,表示逻辑值真、假

    • TrueFalse是唯二两个布尔对象
    • 整形子类型:在各类场合中行为类似整形10,仅在 转换为字符串时返回"True""False"

方法、函数

  • int.bit_length():不包括符号位、开头0位长
  • int.to_bytes(length, byteorder, *, signed=False)
  • class int.from_bytes(bytes, byteorder, *, signed=False)

numbers.Real(float)

float:表示机器级双精度浮点数

  • 接受的取值返回、溢出处理取决于底层结构、python实现
  • python不支持单精度浮点
  • 没必要因为节省处理器、内存消耗而增加语言复杂度

特殊取值

1
2
3
4
5
infty = float("inf")
neg_infty = float("-inf")
# 正/负无穷大
nan = float("nan")
# Not a Number
  • 特殊取值根据定义==is肯定返回False

    • float.__eq__内部应该有做检查,保证==返回False
    • 每次会创建“新”的nan/infty
    • 连续执行id(float("nan"))返回值可能相等,这是因为 每次生成的float("nan")对象被回收,不影响
  • np.nan is np.nan返回True,应该是numpy初始化的时候 创建了一个float("nan"),每次都是使用同一个nan

相关操作

  • float.as_integer_ratio()
  • float.is_integer()
  • float.hex()
  • classmethod float.fromhex(s)
  • round(f[,n])
  • math.trunc(f)
  • math.floor(f)
  • math.ceil(f)

numbers.Complex(complex)

complex:以一对机器级双精度浮点数表示复数值

  • 实部、虚部:可通过只读属性z.realz.imag获取

Iterators

迭代器类型

  • 迭代器对象需要自身支持以下两个方法,其共同组成迭代器协议
    • iterator.__iter__()
    • iterator.__next__()
  • 方法详细参考cs_python/py3ref/cls_special_method

Generator

生成器类型:提供了实现迭代器协议的便捷形式

  • 将容器对象的__iter__()方法实现为生成器,方便实现容器对 迭代器支持
  • 创建、使用参见cs_python/py3ref/dm_gfuncs

序列

序列:表示以非负整数作为索引的有限有序集

  • 不可变序列类型:对象一旦创建不能改变

    • 若包含其他可变对象引用,则可变对象“可改变”
    • 但不可变对象所直接引用的对象集是不可变的
    • 包括
      • str
      • tuple
      • bytes
      • range:非基本序列类型
  • 可变序列:创建后仍可被改变值

    • list
    • bytesarray

通用序列操作

  • x in sx not in s

    • strbytesbytearray支持子序列检测
  • s + t:拼接

    • 拼接不可变总会生成新对象
    • 重复拼接构建序列的运行时开销将基于序列总长度乘方
  • s * nn * ss自身拼接n

    • n<0被当作0处理
    • s中项不会被复制,而是被多次引用
  • s[i]s[i:j]s[i:j:step]

    • i<0索引为负值:索引顺序相对于序列s末尾,等价于 对序列长度取模
    • 序列切片:与序列类型相同的新序列
      • 索引从0开始
      • 左闭右开
    • 某些序列支持a[i:j:step]扩展切片
  • s.index(x[, i[, j]])

    • 仅部分序列支持
    • 类似s[i:j].index(x),但返回值是相对序列开头
  • s.count(x):序列中元素x数目

  • len(s):返回序列条目数量

  • min(s)max(s):序列最小、最大值

  • 序列比较运算默认实现参见cs_python/py3ref/expressions
  • 以上运算自定义实现参见 cs_python/py3ref/cls_special_methods

不可变序列

不可变序列普遍实现而可变序列未实现的操作

  • hash()内置函数

可变序列

  • s[i]=xs[i:j]=ts[i:j:k]=t:下标、切片被赋值
    • s[i:j:k]=tt长度必须和被替换切片长度相同
  • del s[i:j]del s[i:j:k]:移除元素
    • 作为del语句的目标
    • 等同于s[i:j]=[]
  • s.append():添加元素
    • 等同于s[len(s):len(s)] = [x]
  • s.clear():移除所有项
    • 等同于del s[:]
  • s.copy():浅拷贝
    • 等同于s[:]
  • s.extend(t):扩展(合并)序列
    • 基本上等于s += t
  • s.insert(i, x):向序列中插入元素
    • 等同于s[i:i] = [x]
  • s.pop(i=-1):弹出序列中元素
  • s.remove(x):删除序列中首个值为x的项
  • s.reverse():反转序列
    • 反转大尺寸序列时,会原地修改序列
    • 为提醒用户此操作通过间接影响进行,不会返回反转后序列
  • arraycollections模块提供额外可变序列类型
  • 可利用collections.abc.MutableSequence抽象类简化自定义 序列操作

tuple

元组

  • 元组中条目可以是任意python对象
  • 元组创建
    • 一对圆括号创建空元组
    • 逗号分隔
      • 单项元组:后缀逗号a,(a,)
      • 多项元组:a,b,c(a,b,c)
    • 内置构建器:tupletuple(iterable)

list

列表

  • 列表中条目可以是任意python对象
  • 构建方式
    • 方括号括起、项以逗号分隔:[][a][a,b]
    • 列表推导式:[x for x in iterable]
    • 类型构造器:list(iterable)

相关操作

.sort
1
2
def list.sort(*, key=None, reverse=False):
pass
  • 用途:对列表原地排序

    • 使用<进行各项之间比较
    • 不屏蔽异常:若有比较操作失败,整个排序操作将失败, 此时列表可能处于部分被修改状态
  • 参数

    • key:带参数函数,遍历处理每个元素提取比较键
      • None:默认,直接使用列表项排序
  • 说明

    • .sort保序,有利于多重排序
    • 为提醒用户此方法原地修改序列保证空间经济性,其不返回 排序后序列(可考虑使用sorted显式请求)
  • CPython:列表排序期间尝试改变、检测会造成未定义影响, CPython将列表排序期间显式为空,若列表排序期间被改变将 raise ValueError

str

1
2
3
4
5
class str(object="")
# 返回`object.__str__()`、`object.__repr__()`
class str(object=b"", encoding="utf-8", errors="strict")
# 给出`encoding`、`errors`之一,须为bytes-like对象
# 等价于`bytes.decode(encoding, errors)`

字符串:由Unicode码位值组成不可变序列(应该是UTF16-bl编码)

  • 范围在U+0000~U+10FFFF内所有码位值均可在字符串中使用
  • 不存在单个“字符”类型
    • 字符串中单个字符为长度为1字符串
  • 不存在可变字符串类型
    • 可以用str.join()io.StringIO高效连接多个字符串 片段
  • 字符串构建
    • 字符串字面值:cs_python/py3ref/lexical_analysis
    • 内置构造器str()

相关操作

  • ord():转换单个字符字符串为(整形)码位
  • chr():转换(整形)码位为单个字符字符串
判断
  • str.isalnum()
  • str.isalpha()
  • str.isascii()
  • str.isdecimal()
  • str.isdigit()
  • str.isidentifier()
  • str.islower()
  • str.isnumeric()
  • str.isprintable()
  • str.isspace()
  • str.istitle()
  • str.isupper()
查找
  • str.rfind(sub[, start[, end]])
  • str.rindex(sub[, start[, end]])
  • str.startswith(prefix[, start[, end]])
  • str.endwith(suffix[, start[, end]])
  • str.count(sub[, start[, end]]):子串出现次数
  • str.find(sub[, start[, end]])
    • 仅检查sub是否为子串,应使用in
    • 找不到子串时返回-1
  • str.index(sub[, start[, end]])
    • 类似str.find,但找不到子串时raise ValueError
分隔
  • str.partition(sep)
  • str.rpartition(sep)
  • str.rsplit(sep=None, maxsplit=-11)
  • str.split(sep=None, maxsplit=-1)
  • str.splitline([keepends])
拼接
  • str.join(iterable)
  • str.strip([chars])
  • str.lstrip([chars])
  • str.rstrip([chars])
  • str.rstrip([chars])
转换
  • str.lower()
  • str.upper()
  • str.swapcase()
  • str.translate(table)
  • str.replace(old, new[, count])
  • static str.maketrans(x[, y[, z]])
  • str.encode(encoding="utf-8", errors="strict"):使用 指定编码方案编码为bytes
  • str.expandtabs(tabsize=8)
  • str.capitalize():首字符大写副本
  • str.casefold():消除大小写副本
  • str.center(width[, fillchar]):字符串位于中间的字符串
  • str.title()
格式化
  • str.ljust(width[, fillchar])
  • str.rjust(width[, fillchar])
  • str.zfill(width)
  • str.format(*args, **kwargs)
  • str.format_map(mapping)
    • 类似str.format(**mapping),但mapping不会被复制 到dict
      1
      2
      3
      4
      class Default(dict):
      def __missing__(self, key):
      return key
      "{name} was born in {country}".format_map(Default(name="Guido"))
printf风格字符串格式化
  • format % values中:format%转换标记符将被转换 为values中条目

    • 效果类似于sprintf
    • values为与format中指定转换符数量等长元组、或映射 对象,除非format要求单个参数
  • 转换标记符按以下顺序构成

    • %字符:标记转换符起始
    • 映射键:可选,圆括号()括起字符序列
      • values为映射时,映射键必须
    • 转换旗标:可选,影响某些类型转换效果
      • #:值转换使用“替代形式”
      • 0:为数字值填充0字符
      • -:转换值左对齐(覆盖0
      • :符号位转换产生整数(空字符串)将留出空格
      • +:符号字符显示在开头(覆盖
    • 最小字段宽度:可选
      • *:从values读取下个元素
    • 精度:可选,.之后加精度值
      • *:从values读取下个元素
    • 长度修饰符:可选
    • 转换类型
      • d/u/i:十进制整形
      • o:8进制整形
        • #替代形式,前端添加0o
      • x/X:小/大写16进制整形
        • #替代形式,前端添加0x/0X
      • e/E:小/大写浮点指数
        • #替代形式,总是包含小数点
      • f/`F:浮点10进制
        • #替代形式,总是包含小数点
      • g/G:指数小于-4、不小于精度使用指数格式
        • #替代形式,总是包含小数点,末尾0不移除
      • c:单个字符(接收整数、单个字符字符串)
      • r/s/a:字符串(repr/str/ascii转换)
        • 按输出精度截断
      • %:输出%字符

技巧

  • 快速字符串拼接
    • 构建包含字符串的列表,利用str.join()方法
    • 写入io.StringIO实例,结束时获取值

bytes/bytearray

1
class bytes([source[, encoding[, errors]]])
  • 字节串:单个字节构成的不可变序列
  • 字节数组:字节串可变对应版本,其他同不可变bytes
  • 字节串构建

    • 字节串字面值:cs_python/py3ref/lexical_analysis
    • 内置构造器bytes()
      • 指定长度零值填充:bytes(10)
      • 整数组成可迭代对象:bytes(range(20))
      • 通过缓冲区协议复制现有二进制数据:bytes(obj)
  • 字节数组构建

    • 字节数组没有字面值语法,只能通过构造器构造
    • 可变,构建空字节数组有意义
  • 类似整数构成序列

    • 每个条目都是8位字节
    • 取值范围0~255,但只允许ASCII字符0~127
    • b[0]产生整数,切片返回bytes对象
    • 可通过list(bytes)bytes对象转换为整数构成列表
  • memeoryview提供支持

相关函数、方法

  • bytes.decode:解码为相关字符串
  • classmethod bytes.fromhex(string)
  • bytes.hex()

技巧

  • 快速字节串拼接
    • 构建包含字节串的列表,利用bytes.join()方法
    • 写入io.BytesIO实例,结束时获取值
    • 使用betaarray对象进行原地拼接

memoryview

1
class memoryview(obj)

内存视图:允许python代码访问对象内部数据

  • 若对象支持缓冲区协议,则无需拷贝

    • 支持缓冲区协议的内置对象包括bytesbytesarray
  • 内存视图元素:原始对象obj处理的基本内存单元

    • 对简单bytesbytesarray对象,一个元素就是一字节
    • array.array等类型可能有更大元素
  • 内存视图支持索引抽取、切片

    • 若下层对象可选,则支持赋值,但切片赋值不允许改变大小

相关操作

  • mv.__eq__(exporter)
  • mv.__len__()
  • mv.tobyte()
  • mv.hex()
  • mv.tolist()
  • mv.release()
  • mv.cast(format[, shape]):将内存视图转换新格式形状

可用属性

以下属性均只读

  • mv.obj:内存视图的下层对象
  • mv.nbytes
    • == product(shape) * itemsize = len(mv.tobytes())
  • mv.readonly
  • mv.format:内存视图中元素格式
    • 表示为struct模块格式
  • mv.itemsize
  • mv.ndim
  • mv.shape
  • mv.strides
  • mv.suboffsets
  • mv.c_contiguous
  • mv.f_contiguous
  • mv.contiguous

Slices Object

切片对象:表示__getitem__()方法得到的切片

  • 可以使用内置的slice()函数创建
  • a[start: stop]形式的调用被转换为 a[slice(start, stop, None)]
  • 切片对象是内部类型,参见cs_python/py3ref/dm_exec,也 不是序列类型

特殊只读属性

  • start:下界
  • stop:上界
  • step:步长值
  • 属性可以具有任意类型

方法

  • .indices(self, length):计算切片对象被应用到length 长度序列时切片相关信息
    • 返回值:(start, stop, step)三元组
    • 索引号缺失、越界按照正规连续切片方式处理

range

range:不可变数字序列类型(非不是基本序列类型)

1
2
class range(stop)
class range(start=0, stop[, step=1])
  • 参数:必须均为整数(int或实现__index__方法)

    • step > 0:对range对象r[i]=start + step * i,其中 i >= 0, r[i] < stop
    • step < 0:对range对象r[i]=start + step * i,其中 i >= 0, r[i] > stop
    • step = 0raise ValueError
  • 说明

    • 允许元素绝对值大于sys.maxsize,但是某些特性如: len()可能raise OverflowError
    • range类型根据需要计算单项、切片值
      • 相较于常规listtuple占用内存较小,且和表示 范围大小无关
      • 只能表示符合严格模式的序列
    • range类型实现了collections.abc.Sequence抽象类
      • 基本实现序列所有操作:检测、索引查找、切片等
      • 除拼接、重复:拼接、重复通常会违反严格模式
    • !===range对象视为序列比较,即提供相同值即 认为相等

集合类型

  • 表示不重复不可变对象组成的无序、有限集合

    • 不能通过下标索引
    • 可以迭代
    • 可以通过内置函数len返回集合中条目数量
  • 常用于

    • 快速成员检测、去除序列中重复项
    • 进行交、并、差、对称差等数学运算

公用操作

  • len(s)
  • x [not ]in s
  • s.isdisjoint(other)
  • s.issubset(other)/s <= other
  • s < other
  • s.issuperset(other)/s >= other
  • s > other
  • s.union(*others)/s | other |...
  • s.intersection(*others)/s & other &...
  • s.difference(*other)/s - other - other
  • s.symmetric_difference(other)/s ^ other
  • s.copy()
  • 集合比较仅定义偏序,集合列表排序无意义

可变集合独有

  • s.update(*others)/s |= other |...
  • s.intersection_update(*others)/s &= other &...
  • s.difference_udpate(*others)/s -= other |...
  • s.symmetric_difference_update(other)/set ^= other
  • s.add(elem)
  • s.remove(elem)
  • s.discard(elem)
  • s.pop()
  • s.clear()

set/frozenset

1
2
class set([iterable])
class frozenset([iterable])
  • 集合:由具有唯一性的hashable对象组成的多项无序集
  • 冻结集合:不可变集合,可哈希,可以用作集合元素、字典键
  • 创建集合

    • set()内置构造器
    • 花括号包括、逗号分隔元组列表:{a, b}
  • 创建冻结集合

    • frozenset()内置构造器
  • python中集合类似dict通过hash实现

    • 集合元素须遵循同字典键的不可变规则
    • 数字:相等的数字1==1.0,同一集合中只能包含一个

操作说明

  • .remove.__contains__discard等可以接收set类型 参数,其将被转换为临时frozenset对象

  • 非运算符版本操作可以接受任意可迭代对象作为参数,运算符 版本只能接受集合类型作为参数

映射

映射:表示任何索引集合所索引的对象的集合

  • 通过下标a[k]可在映射a中选择索引为k的条目
    • 可在表达式中使用
    • 可以作为赋值语句、del语句的目标
  • dbm.ndbmdbm.gnucollections模块提供额外映射类型

通用操作

  • len(d)
  • d[key]
  • key [not ]in d
  • iter(d)
  • d.keys():返回字典视图对象
  • d.values():返回字典视图对象
  • d.items():返回字典视图对象
  • d.get(key[, default])
  • d.copy()
  • classmethod fromkey(iterable[, value])

可变映射独有

  • d[key]=value
  • del d[key]
  • d.clear()
  • d.setdefault(key[, default])
  • d.pop()
  • d.popitem()
  • d.copy()
  • d.update()

dict

1
2
3
class dict(**kwargs)
class dict(mapping, **kwargs)
class dict(iterable, **kwargs)

字典:可由几乎任意值作为索引的有限个对象可变集合

  • 字典的高效实现要求使用键hash值以保持一致性

    • 不可作为键的值类型
      • 包含列表、字典的值
      • 其他通过对象编号而不是值比较的可变对象
    • 数字:相等的数字1==1.0索引相同字典条目
  • 创建字典

    • 花括号括起、逗号分隔键值对:{key:value,}
    • 内置字典构造器:dict()

字典视图对象

字典视图对象:提供字典条目的动态视图,随字典改变而改变

  • len(dictview)
  • iter(dictview)
  • x in dictview

Rust 标准库数据类型

通用集合类型

Vec<T>

vector允许在单独数据结构中存储多于一个的值,它们在内存中相邻 排列,vector被丢弃时,其中的数据也会被丢弃

存储不同类型

vector只能存储相同类型的值,因为vector必须在编译前知道所有 存储元素所需内存、允许的元素类型,否则对vector进行操作可能 会出错。但是可以使用枚举类型存储”不同类型”(Message为例)

1
vec![Message::Write(String::from("ab"), Message::Move{x:5, y:6}]

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let mut v:Vec<i32> = Vec::new();
let mut v = Vec::with_capacity(3);
//为vector预先分配空间,比`new`稍有效率
//但是这个为啥不用指定类型啊,这样怎么分配空间
let v = vec![1, 2, 3];

v.push(5)
let third:&i32 = &v[2]
//尝试获取一个引用值,如果越界则报错
let third:Option<&i32> = v.get(2)
//尝试获取Option<&i32>,越界则返回None

let mut v = vec![100, 32, 57]
for i in &mut v{
*i += 50;
}

let iter = v.iter();
//不可变引用迭代器
let iter_mut = v.iter_mut();
//可变引用迭代器
let iter_owner = v.into_iter();
//所有权引用迭代器

使用enum+match就能保证处理所有类型,不会出错

字符串

通常意义上的字符串往往是以下两种的”综合“

  • rust核心语言中的字符串slice&str:存储在别处的utf-8 编码字节序列的引用

    字符串slice是&str类型,这个好像体现了rust引用更像 指针,字符串字面值应该是一系列字节序列(流)存储, 所以”返回值“应该是”首地址“,因此是引用类型

  • rust标准库中String类型,是Vec<u8>的封装

    • 可增长
    • 可变
    • 有所有权
    • utf-8编码

索引字符串

因此String类型不能索引获取字符,索引操作预期是常数时间, 而utf-8字列序列并不能保证在常数时间内获取“字符”,rust需要 从头检查。另外,字符串中可能有不可见字符(如发音字符), 即字形簇字符串不等价,此时索引的意义也不明确。

更加有价值的是使用[]和range创建一个字符串slice需要注意的是 如果字符串slice不是有效处utf-8编码序列,程序会在运行时 panic!

1
2
3
4
5
let len = String::from("Здравствуйте").len()
//`len`返回的是utf-8编码序列的长度20,不是“字符”数目12

let hello = "Здравствуйте";
let s = &hello[0..4];

遍历字符串

  • 返回字符Unicode值char类型
    1
    2
    3
    for c in "नमस्ते".chars() {
    println!("{}", c);
    }
    将会打印出6个字符,两个不可见的发音字符
    1
    2
    3
    4
    5
    6






  • 返回字节byte值u8类型
    1
    2
    3
    for b in "नमस्ते".bytes() {
    println!("{}", b);
    }

String常用方法

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
let mut s = String::new();
let s = String::from("initial contet");

let data = "initial content";
let s = data.to_string();
let s = "initial content".to_string();

let mut s = String::from("foo");
s.push_str("bar");
let s2 = "bar";
s.push_str(&s2);
println!(s2);
//此时,`s2`仍然可以打印出来,因为`push_str`的参数是它的
//一个引用,应该是方法中对`&&str`类型做了处理?
s.push('l');

let s1 = String::from("hello, ");
let s2 = String::from("world";
let s3 = s1 + &s2;
//此时`s1`所有权被转移给`s3`不能再使用

let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = string::from("toc");
//let s = s1 + "-" + &s2 + "-" + &s3;
let s = format!("{}-{}-{}", s1, s2, s3);
//`format!`宏是更好地连接字符串的方法,且不会获取任何
//参数的所有权

HashMap

HashMap键、值必须是同质的,相对来说使用频率较低,没有引入 prelude,使用之前需要用use关键字引入

HashMap默认使用一种密码学安全的哈希函数,它可以抵抗拒绝 服务(Denial of Service, DoS)攻击。然而这并不是可用的最快的 算法,不过为了更高的安全性值得付出一些性能的代价。可指定不同 hasher来切换为其它函数。hasher是一个实现了BuildHasher trait的类型

常用函数

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
use std::collections::HashMap;

let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 30);
//对于没有实现`copy`trait的`string`类型,所有权将转移给
//HashMap
scores.insert(String::from("Blue", 20);
//之前存储的值被覆盖

scores.entry(String::from("Yellow")).or_insert(90);
scores.entry(String::from("Red")).or_insert(90);
//`entry`以想要检查的键作为参数,返回`Entry`类型的枚举
//代表可能存在的值,`or_insert`方法在键对应的值存在时
//返回值`Entry`(实际上时值的可变引用),否则将参数作为
//新值插入,并返回修改后的`Entry`

let text = "hello world wonderful word";
let mut map = HashMap::new();
for word in text.split_whitespace(){
let count = map.entry(word).or_insert(0);
*count += 1;
}
//这段将在`map`中存储`text`中个单词出现次数

let teams = vec![String::from("Blue"), String::from("Yello")];
let initial_scores = vec![10, 30];
let scores: HashMap<_,_> = teams.iter.zip(initial_scores.iter()).collect();
//`collect`可能返回很多不同的数据结构,需要显式指定`scores`
//的类型`HashMap<_,_>`

let team_name = String::from("Blue");
let team_score = scores.get(&team_name);

for (key, val) in &scores{
println!("{}:{}", key, val);
}

智能指针

  • 指针pointer:包含内存地址的变量,这个地址引用(指向) 其他数据
  • 智能指针smart pointer:一类数据结构,表现类似于指针, 拥有额外的元数据和功能

Rust中最常见的指针是引用reference,除了引用数据没有其他 特殊功能,也没有任何额外开销。

智能指针通常由结构体实现,区别于常规结构体的特在于实现了 DerefDroptrait

事实上,StringVec<T>也是智能指针

Dereftrait、DerefMuttrait

  • Dereftrait:重载解不可变引用运算符*
  • DerefMuttrait:重载解可变引用引用运算符*

允许智能指针结构体实例表现得像引用,可以让代码兼容智能指针 和引用

1
2
3
4
5
6
7
8
fn main(){
let x = 5;
let y = &x;
let z = Box::new(5);

assert_eq!(5, x);
assert_eq!(5, *y);
assert_eq!(5, *z);

自定义类型实现Dereftrait

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
struct MyBox<T>(T);
//`Box<T>`从本质上被定义为包含一个元素的元组结构体
//类似定义自定义类型`MyBox`
impl<T> MyBox<T>{
fn new(x: T) -> MyBox<T>{
MyBox(X)
}
}

use::std::Deref;
impl<T> Deref for MyBox<T>{
type Target = T;

fn deref(&self) -> &T{
&sell.0
}
//`deref`返回引用,因为大部分使用解引用时不希望获取
//`MyBox`内部值的所有权
}
//为`MyBox<T>`实现`Deref`trait

fn main(){
let x = 5;
let y = MyBox::new(x);

println!("y = {}", *y);
//对于`*y`Rust实际在底层`*(y.deref())`
}

DerefMuttrait类似

隐式解引用强制多态Deref Coercions

将实现了Dereftrait或DerefMuttrait类型的引用转换为其他 类型的引用,通过多次隐式转换使得实参和型参类型 一致,(这些解析发生在编译时,没有运行时损失)避免多次使用 &* 引用和解引用,也使得代码更容易兼容智能指针和引用。

1
2
3
4
5
6
7
fn hello(name: &str){
println!("hello, {}", name);
}
fn main(){
let m = MyBox::new(String::from("Rust"));
hello(&m);
}

实参类型T和型参类型U满足(间接)

  • T: Deref<Target = U>&T转换为&U
  • T: Deref<Target = U>&mut T转换为&U
  • T: DerefMut<Target = U>&mut T转换为&mut U

相当于在引用外面添加任意层&(*_)&mut(*_),直到实参类型 和型参类型一致

Droptrait

Droptrait要求实现drop方法(析构函数destructor),获取 &mut self可变引用,智能指针离开作用域时运行drop方法中的 代码,用于释放类似于文件或网络连接的资源,编译器会自动插入 这些代码。

  • Droptrait会自动清理代码
  • 所有权系统确drop只会在值不再使用时被调用一次

todo:获取&mut self,那么之前不能获取可变引用了?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct CustomSmartPointer{
data: String,
}

impl Drop for CustomSmartPointer{
fn drop(&mut self){
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}

fn main(){
let c = CustomSmartPointer{ data: String::from("pointer1")};
let d = CustomSmartPointer{ data: String::from("pointer2")};
println!("CustomSmartPointer created!");
}

输出顺序如下,变量以被创建时相反的顺序丢弃

1
2
3
CustomSmartPointer created!
Dropping CustomSmartPointer with data `pointer1`!
Dropping CustomSmartPointer with data `pointer2`!

Rust不允许显示调用drop函数,因为Rust仍然会在值离开作用域时 调用drop函数导致double free的错误。如果需要提早清理, 可以使用std::mem::drop函数(已经位于prelude中)。

1
2
3
4
5
6
fn man(){
let c = CustomSmartPointer{ data: String::from("some data")};
println!("CumstomSmartPointer Created");
drop(c);
//调用`std::mem::drop`函数,不是`c.drop()`方法
println!("CustomSmartPointer dropped before the end of main");

Box<T>

在堆上存储数据,而栈上存放指向堆数据的指针,常用于

  • 类型编译时大小未知,而想要在确切大小的上下文中使用
  • 大量数据希望在拷贝时不转移所有权
  • 只关心数据是否实现某个trait而不是其具体的类型 (trait对像)
1
2
3
4
let b = Box::new(5);
println!("b = {}", b);
//可以像数据存储在栈上一样访问数据
//box离开作用域时,栈上和指向的堆上的数据都被释放

创建递归类型

Rust需要在编译时知道类型占用的空间,而递归类型(recursive type)中值的一部分可以时相同类型的另一个值,所以Rust无法知道 递归类型占用的空间。而box大小已知,可以在递归类型中插入box 创建递归类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum List{
Cons(i32, Box<list>),
Nil,
//代表递归终止条件的规范名称,表示列表的终止
//不同于`null`或`nil`
}

use List::{Cons, Nil};

fn main(){
let list = Cons(1,
Box::new(Cons(2,
Box::new(Cons(3,
Box::new(Nil))))));
}

Rc<T>

Rc:引用计数reference counting,记录一个值引用的数量判断 这个值是否仍然被使用,如果值只有0个引用,表示没有任何有效 引用,可以被清理。

Rc<T>允许多个不可变引用,让值有多个所有者共享数据, 引用计数确保任何所有者存在时值有效。用于在堆上分配内存供 程序多个部分读取,且在无法在编译时确定哪部分最后结束使用 (否则令其为所以者即可)

Rc<T>只适合单线程场景

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
enum List{
Cons(i32, Rc<List>),
Nil,
}
//使用`Rc<T>`代替`Box<T>`,可以构造共享List

use List::{Cons, Nil};
use std::rc::Rc;

fn main(){
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
println!("count after creating a = {}", Rc::strong_count(&a));
//1

let b = Cons::(3, Rc::clone(&a));
//`Rc::clone`并不像大多数`clone`方法一样对所有数据
//进行深拷贝,只会增加引用计数,允许`a`和`b`**共享**
//`Rc`中数据的所有权
//这里可以调用`a.clone()`代替`Rc::clone(&a)`,但是
//习惯上使用`Rc::cloen(&a)`
println!("count after creating b = {}", Rc::strong_count(&a));
//2

{
let c = Cons::(4, Rc::clone(&a));
println!("count after creating c = {}", Rc::strong_count(&a));
//3
}

println!("count after c goes out of scope = {}", Rc::strong_count(&a));
//2
}

RefCell<T>

RefCell<T>是一个遵守内部可变性模式的类型,允许通过 不可变引用更改T值。实际上仍然是通过可变引用更改值, 只是获得的T的可变引用在RefCell<T>内部。

RefCell<T>同样只能应用于单线程场景

可以理解为,将Rust静态引用改成时分复用引用,Rust在 运行时进时引用检查,只要保证在运行时任意时刻满足引用规则 即可。

内部可变性interior mutability

Rust中的一个设计模式,允许在有不可变引用时改变数据,这违反 了引用规则,因此在该模式中使用unsafe代码模糊Rust通常的 可变性和引用规则。但是引用规则依然适用,只是在运行时检查, 会带来一定运行时损失。

在确保代码运行时遵守借用规则,即使编译器不能保证,可以选择 使用运用内部可变性模式的类型,涉及的unsafe代码被封装进 安全的API中,外部类型依然不可变

RefRefMut

  • Ref = RefCell<T>.borrow():获取T的不可变引用
  • RefMut = RefCell<T>.borrow_mut():获取T的一个可变引用

RefRefMut均是实现Dereftrait的智能指针,RefCell<T> 记录当前活动的RefRefMut指针,调用borrow时,不可变 引用计数加1,Ref离开作用域时不可变引用计数减1

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
pub trait Messenger{
fn send(&self, msg: &str);
}

pub struct LimitTracker<'a, T:'a + Messenger>{
Messenger:&'a T,
value: usize,
max : usize,
}
//这个结构体有`‘a`和`T`两个泛型参数,且`T`还以生命周期
//注解作为trait bound

impl<'a, T> LimitTracker<a', T>
where T: Messenger{
//这里的`T`就没有加上`‘a`作为trait bound
pub fn new(messenger: &T, max: usize) -> LimitTracker<T>{
LimitTracker{
messenger,
value: 0,
max,
}
}

pub fn set_value(&mut self, value:usize){
self.value = value;
let percentage_of_max = self.max as f64 / self.max as f64;
if percentage_of_max >= 0.75{
self.messenger.send("Warning: over 75% of quota has been used!");
}
}

}

#[cfg(test)]
mod tests{
use supper::*;
use std::cell:RefCell;

struct MockMessenger{
sent_messages: RefCell<Vec<String>>,
}
impl MockMessenger{
fn new() -> MockMessenger{
MockMessenger{ sent_messages: RefCell<vec![]> }
}
}
impl Messenger for MockMessenger{
fn send(&self, message: &str){
self.sent_messages.borrow_mut().push(String::from(message));
}
}

#[test]
fn it_send_an_over_75_percent_warning_message(){
let mock_messenger = MockMessenger::new();
let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
limit_tracker.set_value(80);

assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
}
}

Rc<RefCell<T>>

T值可以修改,且可以被多个所有者拥有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#[derive(Debug)]
enum List{
Cons(Rc<RefCell<i32>>, Rc<List>),
Nil
}

use List::{Cons, Nil};
use std::rc::Rc;
use std::cell::RefCell;

fn main(){
let value = Rc::new(RefCell::new(5));
let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));
let b = Cons(Rc::new(RefCell::new(6)), Rc::clone(&a));
let c = Cons(Rc::new(RefCell::new(10)), Rc::clone(&a));

*value.borrow_value += 10;
println!("a after = {:?}", a);
println!("b after = {:?}", b);
println!("c after = {:?}", c);
}

RefCell<Rc<T>>

T值不能改变,但是Rc<T>整体可以改变,此时可能出现引用循环 ,导致内存泄露。引用循环是程序逻辑上的bug,Rust无法捕获。

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
use List::{Cons, Nil};
use std::rc::Rc;
use std::cell::RefCell;
enum List{
Cons(i32, RefCell<Rc<list>>),
Nil,
}
impl List{
fn tail(&self) -> Option<&RefCell<R<List>>>{
match *self{
Cons(_, ref item) => Some(item),
Nil => None,
}
}
}

fn main(){
let a = Rc::new(Cons(5, RefCell::new(Rc::New(Nil))));
println!("a initial rc count ={}", Rc::strong_count(&a));
//1
println!("a next item = {:?}", a.tail());

let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));
println!("a rc count after b creating = {}", Rc::strong_count(&a));
//2
println!("b initial rc count = {}", Rc::strong_count(&b));
//1
println!("b next item = {:?}"<, b.tail());

if let Some(link) = a.tail(){
*link.borrow_mut() = Rc::clone(&b);
//此时`a`、`b`循环引用,离开作用域时,两个值的
//引用计数因为`a`、`b`被丢弃而减1,但是它们互相
//引用,引用计数保持在1,在堆上不会被丢弃
}
println!("b rc count after changing a = {}", Rc::strong_count(&b));
//2
println!("a rc count after changing a = {}", Rc::strong_count(&a));
//2
}

Weak<T>

强引用Rc<T>代表共享Rc实例的引用,代表所有权关系, 而弱引用Weak<T>不代表所有权关系,不同于Rc<T>使用 strong_count计数,Weak<T>使用weak_count计数,即使 weak_count无需为0,Rc实例也会被清理(只要strong_count 为0)

  • Weak<T>指向的值可能已丢弃,不能像Rc<T>一样直接解引用 ,需要调用upgrade方法返回Option<Rc<T>>
  • Weak<T>避免Rc<T>可能导致的引用循环
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
use std::rc::{Rc, Weak};
use std::cell::RefCell;

#[derive(Debug)]
struct Node{
value: i32,
parent: RefCell<Weak<Node>>,
Children: RefCell<Vec<Rc<Node>>>,
}

fn main(){
let leaf = Rc::new(Node{
value: 3,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});

println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
//strong = 1, weak = 0

{
let branch = Rc::new(Node{
value: 5,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![Rc::clone(&leaf)),
});
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
//`downgrade`返回`Weak<T>`

println!(
"branch strong = {}, weak = {}",
Rc::strong_count(&branch),
Rc::weak_count(&branch),
);
//strong = 1, weak = 1

println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
//strong = 2, weak = 0
}

println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
//`upgrade`返回`Option<Rc<T>>`,此例中因为`branch`
//离开作用域已经丢弃,这里返回`None`
println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
//strong = 1, weak = 0
//如此不会造成引用循环导致内存泄露
}

比较

智能指针 数据拥有者 引用检查 多线程
Box<T> 单一所有者 编译时执行可变(不可变)引用检查
Rc<T> 多个所有者 编译时执行不可变引用检查
RefCell<T> 单一所有者 运行时执行不可变(可变)引用检查
Weak<T> 不拥有数据 编译时执行可变(不可变)检查

常用枚举类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Option<T>{
Some<T>,
None,
}

some = Some(9);
some.take();
//`take`获取`some`的值所有权作为返回值,并设置`some`为
//`None`

Result<T, U>{
Ok<T>,
Err<U>,
}