{"doc_urls":["about.html#以往的锈","about.html#现在的锈","about.html#这本书的读者","daily-dev.html#日常开发常用库","daily-dev.html#目录索引","daily-dev.html#webhttp","daily-dev.html#日志监控","daily-dev.html#sql客户端","daily-dev.html#nosql客户端","daily-dev.html#分布式","daily-dev.html#网络通信协议","daily-dev.html#异步网络编程","daily-dev.html#搜索引擎","daily-dev.html#代码debug","daily-dev.html#性能优化","daily-dev.html#多线程","daily-dev.html#编解码","daily-dev.html#ui-开发框架","daily-dev.html#email","daily-dev.html#常用正则模版","superstar.html#明星项目","superstar.html#deno","superstar.html#alacritty","superstar.html#starship","superstar.html#meilisearch","superstar.html#swc-195k","superstar.html#tauri","superstar.html#yew","superstar.html#firecracker","superstar.html#nushell","superstar.html#tokio","superstar.html#appflowy","superstar.html#bevy","superstar.html#actix-web","superstar.html#iced","superstar.html#cubejs","superstar.html#wasmer","superstar.html#tikv","superstar.html#ruffle","superstar.html#rustdesk","superstar.html#rustpython","superstar.html#vector","superstar.html#mdbook","superstar.html#zola","superstar.html#gitui","superstar.html#solana","superstar.html#ripgrep","superstar.html#citybound","superstar.html#bottlerocket","superstar.html#lemmy","superstar.html#tantivy","superstar.html#sled","superstar.html#redox","superstar.html#youki","superstar.html#sixtyfps","superstar.html#wasmtime","superstar.html#polkadot","superstar.html#lapce","superstar.html#rust-gpu","empowering-js.html#使用rust增强javascript","empowering-js.html#javascript基建库","empowering-js.html#deno","empowering-js.html#swc","empowering-js.html#rome","empowering-js.html#fnm","empowering-js.html#boa","empowering-js.html#napi","empowering-js.html#volt","empowering-js.html#neon","empowering-js.html#resvg-js","empowering-js.html#deno_lint","empowering-js.html#rslint","empowering-js.html#rusty_v8","empowering-js.html#用wasm增强js","empowering-js.html#wasm","empowering-js.html#yew","empowering-js.html#gloo","empowering-js.html#wasm-bindgen","empowering-js.html#wasm-pack","empowering-js.html#wasmer","empowering-js.html#wasmtime","empowering-js.html#trunk","empowering-js.html#photon","empowering-js.html#tinysearch","empowering-js.html#wasm-pdf","empowering-js.html#makepad","empowering-js.html#rust--javascript学习教程","empowering-js.html#wasm-book","empowering-js.html#wasm-learning","empowering-js.html#rust-js-snake-game","games.html#游戏","games.html#目录索引","games.html#veloren","games.html#citybound","games.html#sandspiel","games.html#fish-fight","games.html#doukutsu","games.html#rusted-ruins","games.html#sulis","games.html#zemeroth","games.html#mk48","games.html#theta-wave","games.html#rust-doom","gamedev.html#游戏开发","gamedev.html#目录索引","gamedev.html#游戏引擎","gamedev.html#bevy","gamedev.html#fyrox前rg3d","gamedev.html#ggez","gamedev.html#oxygengine","gamedev.html#macroquad","gamedev.html#godot-rust","gamedev.html#piston","gamedev.html#amethyst","gamedev.html#gpu和图形渲染","gamedev.html#wgpu","gamedev.html#rust-gpu","gamedev.html#kajiya","gamedev.html#lyon","gamedev.html#ash","gamedev.html#vulkano","gamedev.html#rend3","gamedev.html#rafx","gamedev.html#gfx","gamedev.html#luminance","gamedev.html#miniquad","gamedev.html#glow","gamedev.html#学习资料","gamedev.html#游戏开发最新新闻","gamedev.html#一些学习资料英文","gamedev.html#ecsentity-component-system和dod面向数据设计资料","gamedev.html#一些游戏开发的生产力工具","algos/awesome.html#awesome-算法","algos/awesome.html#通用算法","algos/awesome.html#rust-algorithms","algos/awesome.html#thealgorithmsrust","algos/awesome.html#leetcode","algos/awesome.html#rustgym","algos/awesome.html#分布式算法","algos/awesome.html#raft-rs","algos/awesome.html#密码学","algos/awesome.html#rust-crypto","algos/awesome.html#专用算法","algos/awesome.html#rust-bio","algos/randomness.html#生成随机值","algos/randomness.html#生成随机数","algos/randomness.html#指定范围生成随机数","algos/randomness.html#使用指定分布来生成随机数","algos/randomness.html#在自定义类型中生成随机值","algos/randomness.html#生成随机的字符串a-z-a-z-0-9","algos/randomness.html#生成随机的字符串-用户指定-ascii-字符-","algos/sorting.html#vector-排序","algos/sorting.html#对整数-vector-排序","algos/sorting.html#对浮点数-vector-排序","algos/sorting.html#对结构体-vector-排序","algos/compression/tar.html#使用tar包","algos/compression/tar.html#解压-tar-包","algos/compression/tar.html#将目录压缩成-tar-包","algos/compression/tar.html#解压的同时删除指定的文件前缀","algos/cryptography/hashing.html#哈希","algos/cryptography/hashing.html#计算文件的-sha-256-摘要","algos/cryptography/hashing.html#使用-hmac-摘要来签名和验证消息","algos/cryptography/encryption.html#加密","algos/cryptography/encryption.html#使用--pbkdf2-对密码进行哈希和加盐-salt-","algos/math/linear-algebra.html#线性代数","algos/math/linear-algebra.html#矩阵相加","algos/math/linear-algebra.html#矩阵相乘","algos/math/linear-algebra.html#标量向量矩阵相乘","algos/math/linear-algebra.html#向量比较","algos/math/linear-algebra.html#向量范数-norm-","algos/math/linear-algebra.html#矩阵的逆变换","algos/math/linear-algebra.html#序列反序列化一个矩阵","algos/math/trigonometry.html#三角函数","algos/math/trigonometry.html#三角形边长计算","algos/math/trigonometry.html#验证-tan--sin--cos","algos/math/trigonometry.html#地球上两点间的距离","algos/math/complex.html#复数","algos/math/complex.html#创建复数","algos/math/complex.html#复数相加","algos/math/complex.html#数学函数","algos/math/statistics.html#统计","algos/math/statistics.html#测量中心趋势","algos/math/statistics.html#标准偏差","algos/math/misc.html#杂项","algos/math/misc.html#大整数-big-int","datastructures/awesome.html#数据结构","datastructures/bitfield.html#位字段","datastructures/bitfield.html#定义和操作位字段","cmd/awesome.html#命令行工具","cmd/awesome.html#索引目录","cmd/awesome.html#bat","cmd/awesome.html#exa","cmd/awesome.html#lsd","cmd/awesome.html#fd","cmd/awesome.html#procs","cmd/awesome.html#sd","cmd/awesome.html#dust","cmd/awesome.html#starship","cmd/awesome.html#ripgrep","cmd/awesome.html#tokei","cmd/awesome.html#hyperfine","cmd/awesome.html#bottom","cmd/awesome.html#tealdear","cmd/awesome.html#bandwhich","cmd/awesome.html#grex","cmd/awesome.html#zoxide","cmd/awesome.html#delta","cmd/awesome.html#nushell","cmd/awesome.html#mcfly","cmd/awesome.html#fselect","cmd/awesome.html#pueue","cmd/awesome.html#watchexec","cmd/awesome.html#dura","cmd/awesome.html#alacritty","cmd/awesome.html#broot","cmd/parsing.html#参数解析","cmd/parsing.html#clap","cmd/parsing.html#structopt","cmd/ansi.html#ansi-终端","cmd/ansi.html#颜色字体","cmd/ansi.html#加粗字体","cmd/ansi.html#加粗和颜色","os/awesome.html#操作系统","os/awesome.html#目录","os/awesome.html#redox","os/awesome.html#tock","os/awesome.html#theseus","os/awesome.html#writing-an-os-in-rust","os/awesome.html#rust-raspberrypi-os-tutorials","os/awesome.html#rcore-os","os/awesome.html#edu-os","os/processor.html#处理器","os/processor.html#获取逻辑cpu的核心数","os/command.html#调用系统命令","os/command.html#调用一个外部命令并处理输出内容","os/command.html#调用-python-解释器运行代码并检查返回的错误码","os/command.html#通过管道来运行外部命令","os/command.html#将子进程的-stdout-和-stderr-重定向到同一个文件","os/command.html#持续处理子进程的输出","os/command.html#读取环境变量","cocurrency/threads.html#线程","cocurrency/threads.html#生成一个临时性的线程","cocurrency/threads.html#创建并行流水线","cocurrency/threads.html#线程间传递数据","cocurrency/threads.html#维护全局可变的状态","cocurrency/threads.html#并行计算-iso-文件的-sha256","cocurrency/threads.html#使用线程池来绘制分形","cocurrency/parallel.html#任务并行处理","cocurrency/parallel.html#并行修改数组中的元素","cocurrency/parallel.html#并行测试集合中的元素是否满足给定的条件","cocurrency/parallel.html#使用给定条件并行搜索","cocurrency/parallel.html#对数组进行并行排序","cocurrency/parallel.html#并行化-map-reuduce","cocurrency/parallel.html#并行生成缩略图","database/sqlite.html#sqlite","database/sqlite.html#创建-sqlite-数据库","database/sqlite.html#插入和查询","database/sqlite.html#使用事务","database/postgres.html#postgres","database/postgres.html#在数据库中创建表格","database/postgres.html#插入和查询","database/postgres.html#聚合数据","datetime/duration.html#时间计算和转换","datetime/duration.html#测量某段代码的耗时","datetime/duration.html#对日期和时间进行计算","datetime/duration.html#将本地时间转换成其它时区","datetime/parsing.html#解析和显示","datetime/parsing.html#检查日期和时间","datetime/parsing.html#日期和时间戳的相互转换","datetime/parsing.html#显示格式化的日期和时间","datetime/parsing.html#将字符串解析为-datetime-结构体","devtools/log.html#日志","devtools/log.html#log-包","devtools/log.html#在控制台打印-debug-信息","devtools/log.html#将错误日志输出到控制台","devtools/log.html#将错误输出到标准输出-stdout","devtools/log.html#使用自定义-logger","devtools/log.html#输出到-unix-syslog","devtools/log.html#tracing","devtools/config-log.html#配置日志","devtools/config-log.html#为每个模块开启独立的日志级别","devtools/config-log.html#使用自定义环境变量来设置日志","devtools/config-log.html#在日志中包含时间戳","devtools/config-log.html#将日志输出到指定文件","devtools/version.html#版本号","devtools/version.html#解析并增加版本号","devtools/version.html#解析一个复杂的版本号字符串","devtools/version.html#检查给定的版本号是否是预发布","devtools/version.html#找出给定范围内的最新版本","devtools/version.html#检查外部命令的版本号兼容性","devtools/build-tools.html#构建时工具","devtools/build-tools.html#编译并静态链接一个-c-库","devtools/build-tools.html#编译并静态链接一个-c-库","devtools/build-tools.html#为-c-库创建自定义的-define","encoding/strings.html#字符编码","encoding/strings.html#百分号编码-percent-encoding-","encoding/strings.html#将字符串编码为-applicationx-www-form-urlencoded","encoding/strings.html#十六进制编解码","encoding/strings.html#base64-编解码","encoding/csv.html#csv","encoding/csv.html#读取-csv-记录","encoding/csv.html#读取使用了不同分隔符的-csv-记录","encoding/csv.html#基于给定条件来过滤-csv-记录","encoding/csv.html#序列化为-csv","encoding/csv.html#使用-serde-序列化为-csv","encoding/csv.html#csv-列转换","encoding/structured.html#结构化数据","encoding/structured.html#序列和反序列非结构化的json","encoding/structured.html#解析-toml-文件","encoding/structured.html#使用小端字节序来读写整数","files/read-write.html#文件读写","files/read-write.html#迭代文件中的内容行","files/read-write.html#避免对同一个文件进行读写","files/read-write.html#使用内存映射访问文件","files/dir.html#目录访问","files/dir.html#获取24小时内被修改过的文件","files/dir.html#获取给定路径的-loops","files/dir.html#递归查找重复的文件名","files/dir.html#递归查找满足条件的所有文件","files/dir.html#遍历目录跳过隐藏文件","files/dir.html#递归计算给定深度的文件大小","files/dir.html#递归查找所有-png-文件","files/dir.html#查找满足给定正则的所有文件且忽略文件名大小写","memory/global-vars.html#全局变量","memory/global-vars.html#使用-lazy_static-在运行期初始化全局变量","protocols/tcpip.html#tcpip","protocols/tcpip.html#监听-tcp-端口","protocols/tcpip.html#循环接收进入的-tcp-连接","text/regex.html#正则表达式","text/regex.html#验证邮件格式并取出--前的信息","text/regex.html#从文本中提出--开头的标签","text/regex.html#从文本中提取出所有手机号","text/regex.html#通过多个正则来过滤日志文件","text/regex.html#将文本中所有的指定模式替换成另外一种模式","text/string.html#字符串解析","text/string.html#访问-unicode-字符","text/string.html#为自定义结构体实现-fromstr-特征","text/string.html#实现-display-特征"],"index":{"documentStore":{"docInfo":{"0":{"body":15,"breadcrumbs":2,"title":0},"1":{"body":4,"breadcrumbs":2,"title":0},"10":{"body":42,"breadcrumbs":0,"title":0},"100":{"body":3,"breadcrumbs":2,"title":1},"101":{"body":4,"breadcrumbs":3,"title":2},"102":{"body":4,"breadcrumbs":3,"title":2},"103":{"body":1,"breadcrumbs":0,"title":0},"104":{"body":23,"breadcrumbs":0,"title":0},"105":{"body":0,"breadcrumbs":0,"title":0},"106":{"body":3,"breadcrumbs":1,"title":1},"107":{"body":4,"breadcrumbs":1,"title":1},"108":{"body":2,"breadcrumbs":1,"title":1},"109":{"body":3,"breadcrumbs":1,"title":1},"11":{"body":15,"breadcrumbs":0,"title":0},"110":{"body":2,"breadcrumbs":1,"title":1},"111":{"body":5,"breadcrumbs":2,"title":2},"112":{"body":1,"breadcrumbs":1,"title":1},"113":{"body":2,"breadcrumbs":1,"title":1},"114":{"body":0,"breadcrumbs":1,"title":1},"115":{"body":8,"breadcrumbs":1,"title":1},"116":{"body":4,"breadcrumbs":2,"title":2},"117":{"body":3,"breadcrumbs":1,"title":1},"118":{"body":2,"breadcrumbs":1,"title":1},"119":{"body":2,"breadcrumbs":1,"title":1},"12":{"body":13,"breadcrumbs":0,"title":0},"120":{"body":2,"breadcrumbs":1,"title":1},"121":{"body":2,"breadcrumbs":1,"title":1},"122":{"body":1,"breadcrumbs":1,"title":1},"123":{"body":5,"breadcrumbs":1,"title":1},"124":{"body":3,"breadcrumbs":1,"title":1},"125":{"body":4,"breadcrumbs":1,"title":1},"126":{"body":3,"breadcrumbs":1,"title":1},"127":{"body":0,"breadcrumbs":0,"title":0},"128":{"body":1,"breadcrumbs":0,"title":0},"129":{"body":16,"breadcrumbs":0,"title":0},"13":{"body":8,"breadcrumbs":1,"title":1},"130":{"body":41,"breadcrumbs":3,"title":3},"131":{"body":4,"breadcrumbs":0,"title":0},"132":{"body":0,"breadcrumbs":1,"title":1},"133":{"body":0,"breadcrumbs":0,"title":0},"134":{"body":2,"breadcrumbs":2,"title":2},"135":{"body":1,"breadcrumbs":1,"title":1},"136":{"body":0,"breadcrumbs":1,"title":1},"137":{"body":4,"breadcrumbs":1,"title":1},"138":{"body":0,"breadcrumbs":0,"title":0},"139":{"body":6,"breadcrumbs":2,"title":2},"14":{"body":9,"breadcrumbs":0,"title":0},"140":{"body":0,"breadcrumbs":0,"title":0},"141":{"body":2,"breadcrumbs":2,"title":2},"142":{"body":0,"breadcrumbs":0,"title":0},"143":{"body":2,"breadcrumbs":2,"title":2},"144":{"body":0,"breadcrumbs":0,"title":0},"145":{"body":32,"breadcrumbs":0,"title":0},"146":{"body":38,"breadcrumbs":0,"title":0},"147":{"body":30,"breadcrumbs":0,"title":0},"148":{"body":53,"breadcrumbs":0,"title":0},"149":{"body":22,"breadcrumbs":4,"title":4},"15":{"body":10,"breadcrumbs":0,"title":0},"150":{"body":30,"breadcrumbs":1,"title":1},"151":{"body":0,"breadcrumbs":2,"title":1},"152":{"body":18,"breadcrumbs":2,"title":1},"153":{"body":20,"breadcrumbs":2,"title":1},"154":{"body":67,"breadcrumbs":2,"title":1},"155":{"body":0,"breadcrumbs":2,"title":1},"156":{"body":24,"breadcrumbs":2,"title":1},"157":{"body":31,"breadcrumbs":2,"title":1},"158":{"body":42,"breadcrumbs":1,"title":0},"159":{"body":0,"breadcrumbs":0,"title":0},"16":{"body":34,"breadcrumbs":0,"title":0},"160":{"body":67,"breadcrumbs":2,"title":2},"161":{"body":34,"breadcrumbs":1,"title":1},"162":{"body":0,"breadcrumbs":0,"title":0},"163":{"body":72,"breadcrumbs":2,"title":2},"164":{"body":0,"breadcrumbs":0,"title":0},"165":{"body":29,"breadcrumbs":0,"title":0},"166":{"body":20,"breadcrumbs":0,"title":0},"167":{"body":33,"breadcrumbs":0,"title":0},"168":{"body":69,"breadcrumbs":0,"title":0},"169":{"body":53,"breadcrumbs":1,"title":1},"17":{"body":3,"breadcrumbs":1,"title":1},"170":{"body":28,"breadcrumbs":0,"title":0},"171":{"body":28,"breadcrumbs":0,"title":0},"172":{"body":0,"breadcrumbs":0,"title":0},"173":{"body":14,"breadcrumbs":0,"title":0},"174":{"body":11,"breadcrumbs":3,"title":3},"175":{"body":46,"breadcrumbs":0,"title":0},"176":{"body":0,"breadcrumbs":0,"title":0},"177":{"body":15,"breadcrumbs":0,"title":0},"178":{"body":16,"breadcrumbs":0,"title":0},"179":{"body":14,"breadcrumbs":0,"title":0},"18":{"body":3,"breadcrumbs":1,"title":1},"180":{"body":0,"breadcrumbs":0,"title":0},"181":{"body":208,"breadcrumbs":0,"title":0},"182":{"body":90,"breadcrumbs":0,"title":0},"183":{"body":0,"breadcrumbs":0,"title":0},"184":{"body":25,"breadcrumbs":2,"title":2},"185":{"body":0,"breadcrumbs":0,"title":0},"186":{"body":0,"breadcrumbs":0,"title":0},"187":{"body":84,"breadcrumbs":0,"title":0},"188":{"body":1,"breadcrumbs":0,"title":0},"189":{"body":44,"breadcrumbs":0,"title":0},"19":{"body":0,"breadcrumbs":0,"title":0},"190":{"body":2,"breadcrumbs":1,"title":1},"191":{"body":2,"breadcrumbs":1,"title":1},"192":{"body":3,"breadcrumbs":1,"title":1},"193":{"body":13,"breadcrumbs":1,"title":1},"194":{"body":5,"breadcrumbs":1,"title":1},"195":{"body":53,"breadcrumbs":1,"title":1},"196":{"body":3,"breadcrumbs":1,"title":1},"197":{"body":3,"breadcrumbs":1,"title":1},"198":{"body":8,"breadcrumbs":1,"title":1},"199":{"body":1,"breadcrumbs":1,"title":1},"2":{"body":2,"breadcrumbs":2,"title":0},"20":{"body":2,"breadcrumbs":1,"title":0},"200":{"body":4,"breadcrumbs":1,"title":1},"201":{"body":2,"breadcrumbs":1,"title":1},"202":{"body":4,"breadcrumbs":1,"title":1},"203":{"body":3,"breadcrumbs":1,"title":1},"204":{"body":1,"breadcrumbs":1,"title":1},"205":{"body":2,"breadcrumbs":1,"title":1},"206":{"body":3,"breadcrumbs":1,"title":1},"207":{"body":6,"breadcrumbs":1,"title":1},"208":{"body":4,"breadcrumbs":1,"title":1},"209":{"body":28,"breadcrumbs":1,"title":1},"21":{"body":4,"breadcrumbs":2,"title":1},"210":{"body":1,"breadcrumbs":1,"title":1},"211":{"body":18,"breadcrumbs":1,"title":1},"212":{"body":54,"breadcrumbs":1,"title":1},"213":{"body":3,"breadcrumbs":1,"title":1},"214":{"body":1,"breadcrumbs":1,"title":1},"215":{"body":0,"breadcrumbs":0,"title":0},"216":{"body":117,"breadcrumbs":1,"title":1},"217":{"body":1,"breadcrumbs":1,"title":1},"218":{"body":6,"breadcrumbs":1,"title":1},"219":{"body":11,"breadcrumbs":0,"title":0},"22":{"body":3,"breadcrumbs":2,"title":1},"220":{"body":8,"breadcrumbs":0,"title":0},"221":{"body":18,"breadcrumbs":0,"title":0},"222":{"body":1,"breadcrumbs":0,"title":0},"223":{"body":19,"breadcrumbs":0,"title":0},"224":{"body":13,"breadcrumbs":1,"title":1},"225":{"body":9,"breadcrumbs":1,"title":1},"226":{"body":3,"breadcrumbs":1,"title":1},"227":{"body":5,"breadcrumbs":3,"title":3},"228":{"body":5,"breadcrumbs":4,"title":4},"229":{"body":8,"breadcrumbs":2,"title":2},"23":{"body":2,"breadcrumbs":2,"title":1},"230":{"body":9,"breadcrumbs":2,"title":2},"231":{"body":0,"breadcrumbs":0,"title":0},"232":{"body":8,"breadcrumbs":1,"title":1},"233":{"body":0,"breadcrumbs":0,"title":0},"234":{"body":65,"breadcrumbs":0,"title":0},"235":{"body":57,"breadcrumbs":1,"title":1},"236":{"body":65,"breadcrumbs":0,"title":0},"237":{"body":29,"breadcrumbs":2,"title":2},"238":{"body":40,"breadcrumbs":0,"title":0},"239":{"body":24,"breadcrumbs":0,"title":0},"24":{"body":3,"breadcrumbs":2,"title":1},"240":{"body":0,"breadcrumbs":0,"title":0},"241":{"body":45,"breadcrumbs":0,"title":0},"242":{"body":71,"breadcrumbs":0,"title":0},"243":{"body":29,"breadcrumbs":0,"title":0},"244":{"body":47,"breadcrumbs":0,"title":0},"245":{"body":106,"breadcrumbs":2,"title":2},"246":{"body":249,"breadcrumbs":0,"title":0},"247":{"body":0,"breadcrumbs":1,"title":0},"248":{"body":17,"breadcrumbs":1,"title":0},"249":{"body":45,"breadcrumbs":1,"title":0},"25":{"body":9,"breadcrumbs":3,"title":2},"250":{"body":39,"breadcrumbs":1,"title":0},"251":{"body":22,"breadcrumbs":1,"title":0},"252":{"body":86,"breadcrumbs":3,"title":2},"253":{"body":100,"breadcrumbs":1,"title":0},"254":{"body":0,"breadcrumbs":2,"title":1},"255":{"body":47,"breadcrumbs":2,"title":1},"256":{"body":82,"breadcrumbs":1,"title":0},"257":{"body":72,"breadcrumbs":1,"title":0},"258":{"body":0,"breadcrumbs":2,"title":1},"259":{"body":49,"breadcrumbs":1,"title":0},"26":{"body":2,"breadcrumbs":2,"title":1},"260":{"body":73,"breadcrumbs":1,"title":0},"261":{"body":55,"breadcrumbs":1,"title":0},"262":{"body":0,"breadcrumbs":0,"title":0},"263":{"body":16,"breadcrumbs":0,"title":0},"264":{"body":57,"breadcrumbs":0,"title":0},"265":{"body":46,"breadcrumbs":0,"title":0},"266":{"body":0,"breadcrumbs":0,"title":0},"267":{"body":52,"breadcrumbs":0,"title":0},"268":{"body":39,"breadcrumbs":0,"title":0},"269":{"body":36,"breadcrumbs":0,"title":0},"27":{"body":2,"breadcrumbs":2,"title":1},"270":{"body":68,"breadcrumbs":1,"title":1},"271":{"body":0,"breadcrumbs":0,"title":0},"272":{"body":1,"breadcrumbs":1,"title":1},"273":{"body":26,"breadcrumbs":1,"title":1},"274":{"body":24,"breadcrumbs":0,"title":0},"275":{"body":14,"breadcrumbs":1,"title":1},"276":{"body":48,"breadcrumbs":1,"title":1},"277":{"body":37,"breadcrumbs":2,"title":2},"278":{"body":1,"breadcrumbs":1,"title":1},"279":{"body":0,"breadcrumbs":0,"title":0},"28":{"body":2,"breadcrumbs":2,"title":1},"280":{"body":62,"breadcrumbs":0,"title":0},"281":{"body":19,"breadcrumbs":0,"title":0},"282":{"body":36,"breadcrumbs":0,"title":0},"283":{"body":40,"breadcrumbs":0,"title":0},"284":{"body":0,"breadcrumbs":0,"title":0},"285":{"body":61,"breadcrumbs":0,"title":0},"286":{"body":36,"breadcrumbs":0,"title":0},"287":{"body":16,"breadcrumbs":0,"title":0},"288":{"body":56,"breadcrumbs":0,"title":0},"289":{"body":55,"breadcrumbs":0,"title":0},"29":{"body":2,"breadcrumbs":2,"title":1},"290":{"body":1,"breadcrumbs":0,"title":0},"291":{"body":98,"breadcrumbs":1,"title":1},"292":{"body":53,"breadcrumbs":1,"title":1},"293":{"body":56,"breadcrumbs":2,"title":2},"294":{"body":0,"breadcrumbs":0,"title":0},"295":{"body":53,"breadcrumbs":2,"title":2},"296":{"body":30,"breadcrumbs":4,"title":4},"297":{"body":30,"breadcrumbs":0,"title":0},"298":{"body":36,"breadcrumbs":1,"title":1},"299":{"body":0,"breadcrumbs":2,"title":1},"3":{"body":0,"breadcrumbs":0,"title":0},"30":{"body":3,"breadcrumbs":2,"title":1},"300":{"body":78,"breadcrumbs":2,"title":1},"301":{"body":36,"breadcrumbs":2,"title":1},"302":{"body":41,"breadcrumbs":2,"title":1},"303":{"body":24,"breadcrumbs":2,"title":1},"304":{"body":56,"breadcrumbs":3,"title":2},"305":{"body":115,"breadcrumbs":2,"title":1},"306":{"body":0,"breadcrumbs":0,"title":0},"307":{"body":36,"breadcrumbs":1,"title":1},"308":{"body":85,"breadcrumbs":1,"title":1},"309":{"body":54,"breadcrumbs":0,"title":0},"31":{"body":3,"breadcrumbs":2,"title":1},"310":{"body":0,"breadcrumbs":0,"title":0},"311":{"body":28,"breadcrumbs":0,"title":0},"312":{"body":46,"breadcrumbs":0,"title":0},"313":{"body":46,"breadcrumbs":0,"title":0},"314":{"body":0,"breadcrumbs":0,"title":0},"315":{"body":50,"breadcrumbs":1,"title":1},"316":{"body":45,"breadcrumbs":1,"title":1},"317":{"body":26,"breadcrumbs":0,"title":0},"318":{"body":32,"breadcrumbs":0,"title":0},"319":{"body":28,"breadcrumbs":0,"title":0},"32":{"body":3,"breadcrumbs":2,"title":1},"320":{"body":26,"breadcrumbs":0,"title":0},"321":{"body":20,"breadcrumbs":1,"title":1},"322":{"body":25,"breadcrumbs":0,"title":0},"323":{"body":0,"breadcrumbs":0,"title":0},"324":{"body":39,"breadcrumbs":1,"title":1},"325":{"body":0,"breadcrumbs":2,"title":1},"326":{"body":54,"breadcrumbs":2,"title":1},"327":{"body":1,"breadcrumbs":2,"title":1},"328":{"body":0,"breadcrumbs":0,"title":0},"329":{"body":34,"breadcrumbs":0,"title":0},"33":{"body":5,"breadcrumbs":3,"title":2},"330":{"body":46,"breadcrumbs":0,"title":0},"331":{"body":137,"breadcrumbs":0,"title":0},"332":{"body":44,"breadcrumbs":0,"title":0},"333":{"body":48,"breadcrumbs":0,"title":0},"334":{"body":0,"breadcrumbs":0,"title":0},"335":{"body":18,"breadcrumbs":1,"title":1},"336":{"body":84,"breadcrumbs":1,"title":1},"337":{"body":1,"breadcrumbs":1,"title":1},"34":{"body":2,"breadcrumbs":2,"title":1},"35":{"body":2,"breadcrumbs":2,"title":1},"36":{"body":16,"breadcrumbs":2,"title":1},"37":{"body":3,"breadcrumbs":2,"title":1},"38":{"body":3,"breadcrumbs":2,"title":1},"39":{"body":1,"breadcrumbs":2,"title":1},"4":{"body":5,"breadcrumbs":0,"title":0},"40":{"body":3,"breadcrumbs":2,"title":1},"41":{"body":2,"breadcrumbs":2,"title":1},"42":{"body":2,"breadcrumbs":2,"title":1},"43":{"body":2,"breadcrumbs":2,"title":1},"44":{"body":2,"breadcrumbs":2,"title":1},"45":{"body":1,"breadcrumbs":2,"title":1},"46":{"body":4,"breadcrumbs":2,"title":1},"47":{"body":4,"breadcrumbs":2,"title":1},"48":{"body":2,"breadcrumbs":2,"title":1},"49":{"body":2,"breadcrumbs":2,"title":1},"5":{"body":25,"breadcrumbs":1,"title":1},"50":{"body":4,"breadcrumbs":2,"title":1},"51":{"body":37,"breadcrumbs":2,"title":1},"52":{"body":8,"breadcrumbs":2,"title":1},"53":{"body":3,"breadcrumbs":2,"title":1},"54":{"body":2,"breadcrumbs":2,"title":1},"55":{"body":20,"breadcrumbs":2,"title":1},"56":{"body":2,"breadcrumbs":2,"title":1},"57":{"body":4,"breadcrumbs":2,"title":1},"58":{"body":4,"breadcrumbs":3,"title":2},"59":{"body":3,"breadcrumbs":3,"title":1},"6":{"body":17,"breadcrumbs":0,"title":0},"60":{"body":0,"breadcrumbs":3,"title":1},"61":{"body":19,"breadcrumbs":3,"title":1},"62":{"body":10,"breadcrumbs":3,"title":1},"63":{"body":6,"breadcrumbs":3,"title":1},"64":{"body":2,"breadcrumbs":3,"title":1},"65":{"body":2,"breadcrumbs":3,"title":1},"66":{"body":3,"breadcrumbs":3,"title":1},"67":{"body":2,"breadcrumbs":3,"title":1},"68":{"body":33,"breadcrumbs":3,"title":1},"69":{"body":4,"breadcrumbs":4,"title":2},"7":{"body":30,"breadcrumbs":1,"title":1},"70":{"body":26,"breadcrumbs":3,"title":1},"71":{"body":54,"breadcrumbs":3,"title":1},"72":{"body":3,"breadcrumbs":3,"title":1},"73":{"body":0,"breadcrumbs":3,"title":1},"74":{"body":5,"breadcrumbs":3,"title":1},"75":{"body":2,"breadcrumbs":3,"title":1},"76":{"body":45,"breadcrumbs":3,"title":1},"77":{"body":21,"breadcrumbs":4,"title":2},"78":{"body":3,"breadcrumbs":4,"title":2},"79":{"body":16,"breadcrumbs":3,"title":1},"8":{"body":20,"breadcrumbs":1,"title":1},"80":{"body":20,"breadcrumbs":3,"title":1},"81":{"body":2,"breadcrumbs":3,"title":1},"82":{"body":2,"breadcrumbs":3,"title":1},"83":{"body":2,"breadcrumbs":3,"title":1},"84":{"body":3,"breadcrumbs":4,"title":2},"85":{"body":2,"breadcrumbs":3,"title":1},"86":{"body":0,"breadcrumbs":4,"title":2},"87":{"body":3,"breadcrumbs":4,"title":2},"88":{"body":4,"breadcrumbs":4,"title":2},"89":{"body":7,"breadcrumbs":6,"title":4},"9":{"body":17,"breadcrumbs":0,"title":0},"90":{"body":1,"breadcrumbs":1,"title":0},"91":{"body":24,"breadcrumbs":1,"title":0},"92":{"body":6,"breadcrumbs":2,"title":1},"93":{"body":4,"breadcrumbs":2,"title":1},"94":{"body":5,"breadcrumbs":2,"title":1},"95":{"body":3,"breadcrumbs":3,"title":2},"96":{"body":3,"breadcrumbs":2,"title":1},"97":{"body":3,"breadcrumbs":3,"title":2},"98":{"body":1,"breadcrumbs":2,"title":1},"99":{"body":4,"breadcrumbs":2,"title":1}},"docs":{"0":{"body":"每个同学可能都遇到过以下疑惑: 学完 Rust 后,还做了些题,接下可以做些什么? 需要找一个依赖,但是去哪里找?哪些比较好用?哪些有坑?愁啊 要访问一个文件,哎,但记不住代码,要不百度或谷歌一下吧,最后发现结果往往不尽如人意 而 Rusty Book 就是帮助大家解决这些问题的。 在 Rust 元宇宙,夸奖别人的最高境界就是 rusty: 今天你\"锈\"了吗? 你的 Rust 代码好锈啊! 而本书,就是精选了各种开源库和代码片段,帮助大家打造优\"锈\"的 Rust 项目。 以往,想要锈起来,你需要做到以下两步: 为项目挑选 Awesome 依赖库 但是目前已有的 awesome-rust项目有非常大的问题:里面鱼龙混杂,因为它的目的是列出所有项目,但对用户而言,更想看到的是可以在生产中使用的、稳定更新的优秀项目。 在 Cookbook 中查询实用的代码片段,直接复制到项目中 对于开发者而言,Cookbook 非常实用,几乎每一门编程语言都是如此。原因无他:聪明的开发者大部分时间不是在复制粘贴就是在复制粘贴的路上。而 CookBook 恰恰为各种实用场景提供了可供直接复制粘贴的代码,例如网络协议、数据库和文件操作、随机数生成、命令行解析等。 但目前的 Rust Cookbook 更新非常不活跃,里面缺少了大量实用库,还有一些过时的老库。","breadcrumbs":"Rusty Book » 以往的锈","id":"0","title":"以往的锈"},"1":{"body":"鉴于以上痛点,我们决定打造一本真正的锈书:一本足够\"锈\"但是又不会锈的书。 这本书其实就是 Awesome Rust + Rust Cookbook 的结合体,但是我们不是简单粗暴的对内容进行了合并,而是从深层次将两者进行了融合,希望大家能喜欢。","breadcrumbs":"Rusty Book » 现在的锈","id":"1","title":"现在的锈"},"10":{"body":"Websocket snapview/tokio-tungstenite 更适合Web应用使用的生产级Websocket库,它是异步非阻塞的,基于基于下下面的tungstenite-rs库和tokio实现 rust-websocket 老牌Websocket库,提供了客户端和服务器端实现,但是。。。很久没更新了 snapview/tungstenite-rs 轻量级的Websocket流实现,该库更偏底层,例如,你可以用来构建其它网络库 gRPC hyperium/tonic 纯Rust实现的gRPC客户端和服务器端,支持async/await异步调用,文档和示例较为清晰 tikv/grpc-rs 国产开源之光Tidb团队出品的gRPC框架, 基于C的代码实现, 就是最近好像不是很活跃 其实这两个实现都很优秀,把tonic放在第一位,主要是因为它是纯Rust实现,同时社区也更为活跃,但是并不代表它比tikv的更好! tokio-rs/prost 纯Rust实现的 Protocol Buffers 类库,Prost 支持从 proto2 和 proto3 文件生成简单、实用的代码。 QUIC cloudflare/quiche 大名鼎鼎cloudflare提供的QUIC实现,据说在公司内部重度使用,有了大规模生产级别的验证,非常值得信任,同时该库还实现了HTTP/3 quinn-rs/quinn 提供异步API调用,纯Rust实现,同时提供了几个有用的网络库 MQTT bytebeamio/rumqtt MQTT3.1.1/5协议库,同时实现了客户端与服务器端broker ntex-rs/ntex-mqtt 客户端与服务端框架,支持MQTT3.1.1与5协议 eclipse/paho.mqtt.rust 老牌MQTT框架,对MQTT支持较全, 其它各语言的实现也有","breadcrumbs":"日常开发常用库 » 网络、通信协议","id":"10","title":"网络、通信协议"},"100":{"body":"mk48 是一个2D海战游戏,支持多人在线和和多语言(包括中文),你可以通过官方网址在线试玩: https://mk48.io 。","breadcrumbs":"Rust开发的游戏 » mk48","id":"100","title":"mk48"},"101":{"body":"Theta Wave 是一款2D太空射击游戏,基于Amethyst引擎开发。 在游戏中,你扮演的是保卫目标的飞船,游戏的目标是通过摧毁敌人来存活,还可以收集货币用于购买对你的生存有帮助的物品,并击败最终BOSS取得胜利。","breadcrumbs":"Rust开发的游戏 » theta wave","id":"101","title":"theta wave"},"102":{"body":"Rust doom 是一款模仿Doom 1&2的简单射击游戏,需要注意,它并不是一个Doom移植版。","breadcrumbs":"Rust开发的游戏 » rust doom","id":"102","title":"rust doom"},"103":{"body":"我在这里大胆预言:Rust未来会成为和C++同级别的游戏开发语言,特别是在游戏引擎方面,会大放异彩。","breadcrumbs":"游戏引擎 » 游戏开发","id":"103","title":"游戏开发"},"104":{"body":"游戏引擎 : bevy , fyrox , ggez , oxygengine , macroquad , godot-rust , piston , amethyst GPU和图形渲染 : wgpu , rust-gpu , kajiya , lyon , ash , vulkano , rend3 , rafx , gfx , luminance , miniquad , glow 学习资料和新闻","breadcrumbs":"游戏引擎 » 目录索引","id":"104","title":"目录索引"},"105":{"body":"","breadcrumbs":"游戏引擎 » 游戏引擎","id":"105","title":"游戏引擎"},"106":{"body":"bevy 是一个数据驱动的游戏引擎,支持2D和3D图形开发,优点是社区活跃、更新快、模块化设计优秀、性能高,缺点是还处于快速开发中,并不适合生产使用。 同时bevy的文档齐全,官方示例很多,非常适合学习和使用。","breadcrumbs":"游戏引擎 » Bevy","id":"106","title":"Bevy"},"107":{"body":"fyrox 是一个2D和3D游戏图形化引擎,功能丰富,生产可用(官方宣称)。 该项目前身是rg3d,但是被收购后,更名为fyrox,潜力应该是相当好的,下面截图来源于基于该引擎开发的游戏 StationIapetus 。","breadcrumbs":"游戏引擎 » Fyrox(前rg3d)","id":"107","title":"Fyrox(前rg3d)"},"108":{"body":"ggez 是一个轻量级的2D游戏图形引擎,它的目标是让游戏开发尽量的简单,因此它的功能并不是很强大,例如如果你想要强大且真实的物理引擎,它可能无能为力,但你可以选择在它的基础上构建自己的更高级的引擎。","breadcrumbs":"游戏引擎 » ggez","id":"108","title":"ggez"},"109":{"body":"oxygengine 是一个2D HTML5游戏引擎,支持编译成WASM在浏览器中运行。","breadcrumbs":"游戏引擎 » oxygengine","id":"109","title":"oxygengine"},"11":{"body":"tokio-rs/tokio 最火的异步网络库,除了复杂上手难度高一些外,没有其它大的问题。同时tokio团队提供了多个非常优秀的Rust库,整个生态欣欣向荣,用户认可度很高 async-std 跟标准库API很像的异步网络库,相对简单易用,但是貌似开发有些停滞,还有就是功能上不够完善。但是对于普通用户来说,这个库非常值得一试,它在功能和简单易用上取得了很好的平衡 actix 基于Actor模型的异步网络库,但这个库的开发貌似已经停滞,他们团队一直在专注于actix-web的开发 mio 严格来说,MIO与之前三个不是同一个用途的,MIO = Meta IO,是一个底层IO库,往往用于构建其它网络库,当然如果你对应用网络性能有非常极限的要求, 可以考虑它,因为它的层次比较低,所带来的抽象负担小,所以性能损耗小 如果你要开发生产级别的项目,我推荐使用tokio,稳定可靠,功能丰富,控制粒度细;自己的学习项目或者没有那么严肃的开源项目,我推荐async-std,简单好用,值得学习;当你确切知道需要Actor网络模型时,就用actix","breadcrumbs":"日常开发常用库 » 异步网络编程","id":"11","title":"异步网络编程"},"110":{"body":"macroquad 是一个2D游戏引擎,特点是简单易用,例如它试图让使用者不会遇到Rust生命周期的难题。","breadcrumbs":"游戏引擎 » macroquad","id":"110","title":"macroquad"},"111":{"body":"godot-rust 是大名鼎鼎的godot引擎的Rust绑定, godot 是c++开发的游戏2D/3D引擎,但是对Rust语言提供了很好的支持。","breadcrumbs":"游戏引擎 » godot-rust","id":"111","title":"godot-rust"},"112":{"body":"piston 是前两年较火的模块化的游戏引擎,但是最近半年开发速度缓慢,我调查了一番,但不清楚发生了什么。","breadcrumbs":"游戏引擎 » piston","id":"112","title":"piston"},"113":{"body":"Amethyst , 前几年较火的Rust游戏引擎,但是最近开发已经停滞,经过我调查,是因为作者团队转型Rust游戏开发知识分享,因此项目被 放弃 。","breadcrumbs":"游戏引擎 » Amethyst","id":"113","title":"Amethyst"},"114":{"body":"","breadcrumbs":"游戏引擎 » GPU和图形渲染","id":"114","title":"GPU和图形渲染"},"115":{"body":"wgpu 是一个纯Rust实现的图形化API库,具有安全、可移植等优点,如果你使用基于wgpu构建的库,那该库可以很多平台上运行:Linux, windows, MacOS, Android和IOS。 它可以原生的运行在Vulkan, Metal等主流平台上,且可以使用wasm的方式运行在WebGPU上,同时API兼容WebGPU标准。 总之,如果你要使用WebGPU, 选它就对了。","breadcrumbs":"游戏引擎 » wgpu","id":"115","title":"wgpu"},"116":{"body":"rust-gpu 的目标是让Rust成为GPU编程的第一梯队语言,由大名鼎鼎的Embark公司开发,后台较硬。 如果需要通用的GPU编程,选它就对了。","breadcrumbs":"游戏引擎 » rust-gpu","id":"116","title":"rust-gpu"},"117":{"body":"kajiya 是一个实时的、全局光照渲染系统,由Embark公司开发,该公司在秘密研究急于Rust的游戏引擎,据说准备应用在新游戏上,有朝一日它可能会是推动Rust游戏引擎爆发式发展的功臣。 kajiya应用了非常先进的论文和设计理念,因此非常值得有志于游戏引擎开发的同学学习。但目前还不适用于生产级使用,具体见 这里 。","breadcrumbs":"游戏引擎 » kajiya","id":"117","title":"kajiya"},"118":{"body":"lyon 可以使用GPU进行向量路径渲染,例如高效渲染复杂的svg等。","breadcrumbs":"游戏引擎 » lyon","id":"118","title":"lyon"},"119":{"body":"ash 是一个轻量级的Vulkan绑定。","breadcrumbs":"游戏引擎 » ash","id":"119","title":"ash"},"12":{"body":"ElasticSearch客户端 elastic/elasticsearch 官方es客户端,目前第三方的基本都处于停滞状态,所以不管好坏,用呗 Rust搜索引擎 Tantivy Tantivy是Rust实现的本地搜索库,功能对标lucene,如果你不需要分布式,那么引入tantivy作为自己本地Rust服务的一个搜索,是相当不错的选择,该库作者一直很活跃,而且最近还创立了搜索引擎公司,感觉大有作为. 该库的优点在于纯Rust实现,性能高(lucene的2-3倍),资源占用低(对比java自然不是一个数量级),社区活跃。 Rust搜索平台 quickwit 对标ElasticSearch,一个通用目的的分布式搜索平台,目前还在起步阶段(0.2版本),未来非常可期,目前还不建议使用 MeiliSearch 虽然也是一个搜索平台,但是并不是通用目的的,MeiliSearch目标是为终端用户提供边输入边提示的即刻搜索功能,因此是一个轻量级搜索平台,不适用于数据量大时的搜索目的。总之,如果你需要在网页端或者APP为用户提供一个搜索条,然后支持输入容错、前缀搜索时,就可以使用它。","breadcrumbs":"日常开发常用库 » 搜索引擎","id":"12","title":"搜索引擎"},"120":{"body":"vulkano 是一个安全、特性丰富的Vulkan绑定。","breadcrumbs":"游戏引擎 » vulkano","id":"120","title":"vulkano"},"121":{"body":"rend3 是一个简单易用、可定制性强、高效的3D渲染库,基于wgpu开发。","breadcrumbs":"游戏引擎 » rend3","id":"121","title":"rend3"},"122":{"body":"rafx 是一个多后端渲染器,目标是性能、扩展性和生产力。","breadcrumbs":"游戏引擎 » rafx","id":"122","title":"rafx"},"123":{"body":"gfx 是一个底层的图形库,目前已经不怎么活跃,主要原因是:它的核心组件gfx-hal最开始的目标是为wgpu提供功能,但是后面wgpu实现了自己的wgpu-hal,因此gfx-hal目前仅处于维护状态。","breadcrumbs":"游戏引擎 » gfx","id":"123","title":"gfx"},"124":{"body":"luminance 是一个类型安全、无状态的图形框架,目标是让图形渲染变得简单和优雅,最开始是通过Haskell语言实现,然后在2016年移植到Rust上。 它很简单,功能也不够强大,如果你没有OpenGL、Vulkan的经验,可以使用它做一些简单的图形渲染项目试试。","breadcrumbs":"游戏引擎 » luminance","id":"124","title":"luminance"},"125":{"body":"miniquad 是一个安全和跨平台的图形渲染库,它提供了较为底层的API,如果需要抽象层次更高的API,可以使用之前提到的 macroquad ,后者是基于miniquad封装实现。","breadcrumbs":"游戏引擎 » miniquad","id":"125","title":"miniquad"},"126":{"body":"glow 提供了各种GL绑定(OpenGL, WebGL), 提供了一定的抽象,避免你写平台相关的特定代码实现。","breadcrumbs":"游戏引擎 » glow","id":"126","title":"glow"},"127":{"body":"","breadcrumbs":"游戏引擎 » 学习资料","id":"127","title":"学习资料"},"128":{"body":"gamedev","breadcrumbs":"游戏引擎 » 游戏开发最新新闻","id":"128","title":"游戏开发最新新闻"},"129":{"body":"Hands-on Rust 使用 bracket-lib 和其 配套书籍 进行学习 想要没有困难的开发一个跨平台的2D游戏?使用 macroquad ,并且可以参考用它开发的两个游戏: fish fight 和 zemeroth 想要开发一个简单的3D游戏并且需要一个编辑器?可以试试 fyrox(rg3d) 想要开发一个复杂的游戏或者想要做一个demo,未来可以基于该demo继续开发,最终完成一个复杂游戏?可以试试godot引擎提供的Rust绑定: godot-rust 喜欢钻研前沿技术?试试 bevy ,它拥有最好的ECS实现和最先进的设计理念(可能)","breadcrumbs":"游戏引擎 » 一些学习资料(英文)","id":"129","title":"一些学习资料(英文)"},"13":{"body":"GDB gdbgui 提供浏览器支持的gdb debug工具,支持C,C++,Rust和Go. LLDB CodeLLDB — 专门为VSCode设计的LLDB Debug扩展","breadcrumbs":"日常开发常用库 » 代码Debug","id":"13","title":"代码Debug"},"130":{"body":"我们在上面提到的很多系统都使用了ECS和DOD,因此这两者对于游戏开发是极其重要的,下面是一些相关的英文资料(部分需要翻墙),可以帮助大家理解相关概念。 hecs , 一个用Rust实现的ECS世界 Understanding data-oriented design for entity component systems - Unity at GDC 2019 CppCon 2018: Stoyan Nikolov “OOP Is Dead, Long Live Data-oriented Design” RustConf 2018 - Closing Keynote - Using Rust For Game Development by Catherine West \"Data-Oriented Design\" web book by Richard Fabian","breadcrumbs":"游戏引擎 » ECS(Entity Component System)和DOD(面向数据设计)资料","id":"130","title":"ECS(Entity Component System)和DOD(面向数据设计)资料"},"131":{"body":"Blender 用于3D建模 Krita 用于创建2D图片","breadcrumbs":"游戏引擎 » 一些游戏开发的生产力工具","id":"131","title":"一些游戏开发的生产力工具"},"132":{"body":"","breadcrumbs":"实用算法 » Awesome 算法","id":"132","title":"Awesome 算法"},"133":{"body":"","breadcrumbs":"实用算法 » 通用算法","id":"133","title":"通用算法"},"134":{"body":"rust-algorithms 收集了一些经典的算法和数据结构,更强调算法实现的美观性,因此该库更适用于教学目的,请不要把它当成一个实用算法库在生产环境使用。","breadcrumbs":"实用算法 » rust-algorithms","id":"134","title":"rust-algorithms"},"135":{"body":"TheAlgorithms/Rust 项目所属的 组织 使用各种语言实现了多种算法,但是仅适用于演示的目的。","breadcrumbs":"实用算法 » TheAlgorithms/Rust","id":"135","title":"TheAlgorithms/Rust"},"136":{"body":"","breadcrumbs":"实用算法 » Leetcode","id":"136","title":"Leetcode"},"137":{"body":"rustgym 实现了相当多的 leetcode 和 Avent of Code 题解。","breadcrumbs":"实用算法 » rustgym","id":"137","title":"rustgym"},"138":{"body":"","breadcrumbs":"实用算法 » 分布式算法","id":"138","title":"分布式算法"},"139":{"body":"raft-rs 是由 Tikv 提供的 Raft 分布式算法实现。 Raft 是一个强一致性的分布式算法,比 Paxos 协议更简单、更好理解","breadcrumbs":"实用算法 » raft-rs","id":"139","title":"raft-rs"},"14":{"body":"bheisler/criterion.rs 比官方提供的benchmark库更好,目前已经成为事实上标准的性能测试工具 Bytehound Linux下的内存分析工具,可以用来分析:内存泄漏、内存分配、调用栈追踪,甚至它还有一个浏览器UI! 懂的人都懂,性能测试工具的UI服务是多么稀缺和珍贵! llogiq/flame 专为Rust打造的火焰图分析工具,可以告诉你程序在哪些代码上花费的时间过多,非常适合用于代码性能瓶颈的分析。与perf不同,flame库允许你自己定义想要测试的代码片段,只需要在代码前后加上相应的指令即可,非常好用 sharkdp/hyperfine 一个命令行benchmark工具,支持任意shell命令,支持缓存清除、预热、多次运行统计分析等,尽量保证结果的准确性","breadcrumbs":"日常开发常用库 » 性能优化","id":"14","title":"性能优化"},"140":{"body":"","breadcrumbs":"实用算法 » 密码学","id":"140","title":"密码学"},"141":{"body":"Rust Crypto 提供了一些常用的密码学算法实现,更新较为活跃。","breadcrumbs":"实用算法 » Rust Crypto","id":"141","title":"Rust Crypto"},"142":{"body":"","breadcrumbs":"实用算法 » 专用算法","id":"142","title":"专用算法"},"143":{"body":"rust-bio 有常用的生物信息学所需的算法和数据结构。","breadcrumbs":"实用算法 » rust-bio","id":"143","title":"rust-bio"},"144":{"body":"","breadcrumbs":"实用算法 » 生成随机值 » 生成随机值","id":"144","title":"生成随机值"},"145":{"body":"使用 rand::thread_rng 可以获取一个随机数生成器 rand::Rng ,该生成器需要在每个线程都初始化一个。 整数的随机分布范围等于类型的取值范围,但是浮点数只分布在 [0, 1) 区间内。 use rand::Rng; fn main() { let mut rng = rand::thread_rng(); let n1: u8 = rng.gen(); let n2: u16 = rng.gen(); println!(\"Random u8: {}\", n1); println!(\"Random u16: {}\", n2); println!(\"Random u32: {}\", rng.gen::()); println!(\"Random i32: {}\", rng.gen::()); println!(\"Random float: {}\", rng.gen::());\n}","breadcrumbs":"实用算法 » 生成随机值 » 生成随机数","id":"145","title":"生成随机数"},"146":{"body":"使用 Rng::gen_range 生成 [0, 10) 区间内的随机数( 右开区间,不包括 10 )。 use rand::Rng; fn main() { let mut rng = rand::thread_rng(); println!(\"Integer: {}\", rng.gen_range(0..10)); println!(\"Float: {}\", rng.gen_range(0.0..10.0));\n} Uniform 可以用于生成均匀分布uniform distribution的随机数。当需要在同一个范围内重复生成随机数时,该方法虽然和之前的方法效果一样,但会更快一些。 use rand::distributions::{Distribution, Uniform}; fn main() { let mut rng = rand::thread_rng(); let die = Uniform::from(1..7); loop { let throw = die.sample(&mut rng); println!(\"Roll the die: {}\", throw); if throw == 6 { break; } }\n}","breadcrumbs":"实用算法 » 生成随机值 » 指定范围生成随机数","id":"146","title":"指定范围生成随机数"},"147":{"body":"默认情况下,rand 包使用均匀分布来生成随机数,而 rand_distr 包提供了其它类型的分布方式。 首先,你需要获取想要使用的分布的实例,然后在 rand::Rng 的帮助下使用 Distribution::sample 对该实例进行取样。 如果想要查询可用的分布列表,可以访问 这里 ,下面的示例中我们将使用 Normal 分布: use rand_distr::{Distribution, Normal, NormalError};\nuse rand::thread_rng; fn main() -> Result<(), NormalError> { let mut rng = thread_rng(); let normal = Normal::new(2.0, 3.0)?; let v = normal.sample(&mut rng); println!(\"{} is from a N(2, 9) distribution\", v); Ok(())\n}","breadcrumbs":"实用算法 » 生成随机值 » 使用指定分布来生成随机数","id":"147","title":"使用指定分布来生成随机数"},"148":{"body":"使用 Distribution 特征包裹我们的自定义类型,并为 Standard 实现该特征,可以为自定义类型的指定字段生成随机数。 use rand::Rng;\nuse rand::distributions::{Distribution, Standard}; #[derive(Debug)]\nstruct Point { x: i32, y: i32,\n} impl Distribution for Standard { fn sample(&self, rng: &mut R) -> Point { let (rand_x, rand_y) = rng.gen(); Point { x: rand_x, y: rand_y, } }\n} fn main() { let mut rng = rand::thread_rng(); // 生成一个随机的 Point let rand_point: Point = rng.gen(); println!(\"Random Point: {:?}\", rand_point); // 通过类型暗示( hint )生成一个随机的元组 let rand_tuple = rng.gen::<(i32, bool, f64)>(); println!(\"Random tuple: {:?}\", rand_tuple);\n}","breadcrumbs":"实用算法 » 生成随机值 » 在自定义类型中生成随机值","id":"148","title":"在自定义类型中生成随机值"},"149":{"body":"通过 Alphanumeric 采样来生成随机的 ASCII 字符串,包含从 A-Z, a-z, 0-9 的字符。 use rand::{thread_rng, Rng};\nuse rand::distributions::Alphanumeric; fn main() { let rand_string: String = thread_rng() .sample_iter(&Alphanumeric) .take(30) .map(char::from) .collect(); println!(\"{}\", rand_string);\n}","breadcrumbs":"实用算法 » 生成随机值 » 生成随机的字符串(A-Z, a-z, 0-9)","id":"149","title":"生成随机的字符串(A-Z, a-z, 0-9)"},"15":{"body":"消息通道channel crossbeam-channel , 老牌强库,功能较全,性能较强,之前是独立的库,但是后面合并到了crossbeam主仓库中 flume , 官方给出的性能数据要比crossbeam更好些,但是貌似最近没怎么更新 并发原语(锁) parking_lot , 社区较为活跃,star较多,更新较为活跃 spin , 在多数场景中性能比parking_lot高一点,最近没怎么更新 如果不是追求特别极致的性能,建议选择前者","breadcrumbs":"日常开发常用库 » 多线程","id":"15","title":"多线程"},"150":{"body":"通过 gen_string 生成随机的 ASCII 字符串,包含用户指定的字符。 fn main() { use rand::Rng; const CHARSET: &[u8] = b\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\\ abcdefghijklmnopqrstuvwxyz\\ 0123456789)(*&^%$#@!~\"; const PASSWORD_LEN: usize = 30; let mut rng = rand::thread_rng(); let password: String = (0..PASSWORD_LEN) .map(|_| { let idx = rng.gen_range(0..CHARSET.len()); CHARSET[idx] as char }) .collect(); println!(\"{:?}\", password);\n}","breadcrumbs":"实用算法 » 生成随机值 » 生成随机的字符串( 用户指定 ASCII 字符 )","id":"150","title":"生成随机的字符串( 用户指定 ASCII 字符 )"},"151":{"body":"","breadcrumbs":"实用算法 » Vec 排序 » Vector 排序","id":"151","title":"Vector 排序"},"152":{"body":"以下示例使用 Vec::sort 来排序,如果大家希望获得更高的性能,可以使用 Vec::sort_unstable ,但是该方法无法保留相等元素的顺序。 fn main() { let mut vec = vec![1, 5, 10, 2, 15]; vec.sort(); assert_eq!(vec, vec![1, 2, 5, 10, 15]);\n}","breadcrumbs":"实用算法 » Vec 排序 » 对整数 Vector 排序","id":"152","title":"对整数 Vector 排序"},"153":{"body":"浮点数数组可以使用 Vec::sort_by 和 PartialOrd::partial_cmp 进行排序。 fn main() { let mut vec = vec![1.1, 1.15, 5.5, 1.123, 2.0]; vec.sort_by(|a, b| a.partial_cmp(b).unwrap()); assert_eq!(vec, vec![1.1, 1.123, 1.15, 2.0, 5.5]);\n}","breadcrumbs":"实用算法 » Vec 排序 » 对浮点数 Vector 排序","id":"153","title":"对浮点数 Vector 排序"},"154":{"body":"以下示例中的结构体 Person 将实现基于字段 name 和 age 的自然排序。为了让 Person 变为可排序的,我们需要为其派生 Eq、PartialEq、Ord、PartialOrd 特征,关于这几个特征的详情,请见 这里 。 当然,还可以使用 vec:sort_by 方法配合一个自定义比较函数,只按照 age 的维度对 Person 数组排序。 #[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]\nstruct Person { name: String, age: u32\n} impl Person { pub fn new(name: String, age: u32) -> Self { Person { name, age } }\n} fn main() { let mut people = vec![ Person::new(\"Zoe\".to_string(), 25), Person::new(\"Al\".to_string(), 60), Person::new(\"John\".to_string(), 1), ]; // 通过派生后的自然顺序(Name and age)排序 people.sort(); assert_eq!( people, vec![ Person::new(\"Al\".to_string(), 60), Person::new(\"John\".to_string(), 1), Person::new(\"Zoe\".to_string(), 25), ]); // 只通过 age 排序 people.sort_by(|a, b| b.age.cmp(&a.age)); assert_eq!( people, vec![ Person::new(\"Al\".to_string(), 60), Person::new(\"Zoe\".to_string(), 25), Person::new(\"John\".to_string(), 1), ]); }","breadcrumbs":"实用算法 » Vec 排序 » 对结构体 Vector 排序","id":"154","title":"对结构体 Vector 排序"},"155":{"body":"","breadcrumbs":"实用算法 » 压缩算法 » 使用.tar包 » 使用tar包","id":"155","title":"使用tar包"},"156":{"body":"以下代码将解压缩( GzDecoder )当前目录中的 archive.tar.gz ,并将所有文件抽取出( Archive::unpack )来后当入到当前目录中。 use std::fs::File;\nuse flate2::read::GzDecoder;\nuse tar::Archive; fn main() -> Result<(), std::io::Error> { let path = \"archive.tar.gz\"; let tar_gz = File::open(path)?; let tar = GzDecoder::new(tar_gz); let mut archive = Archive::new(tar); archive.unpack(\".\")?; Ok(())\n}","breadcrumbs":"实用算法 » 压缩算法 » 使用.tar包 » 解压 tar 包","id":"156","title":"解压 tar 包"},"157":{"body":"以下代码将 /var/log 目录压缩成 archive.tar.gz: 创建一个 File 文件,并使用 GzEncoder 和 tar::Builder 对其进行包裹 通过 Builder::append_dir_all 将 /var/log 目录下的所有内容添加到压缩文件中,该文件在 backup/logs 目录下。 GzEncoder 负责在写入压缩文件 archive.tar.gz 之前对数据进行压缩。 use std::fs::File;\nuse flate2::Compression;\nuse flate2::write::GzEncoder; fn main() -> Result<(), std::io::Error> { let tar_gz = File::create(\"archive.tar.gz\")?; let enc = GzEncoder::new(tar_gz, Compression::default()); let mut tar = tar::Builder::new(enc); tar.append_dir_all(\"backup/logs\", \"/var/log\")?; Ok(())\n}","breadcrumbs":"实用算法 » 压缩算法 » 使用.tar包 » 将目录压缩成 tar 包","id":"157","title":"将目录压缩成 tar 包"},"158":{"body":"遍历目录中的文件 Archive::entries ,若解压前的文件名包含 bundle/logs 前缀,需要将前缀从文件名移除( Path::strip_prefix )后,再解压。 use std::fs::File;\nuse std::path::PathBuf;\nuse flate2::read::GzDecoder;\nuse tar::Archive; fn main() -> Result<()> { let file = File::open(\"archive.tar.gz\")?; let mut archive = Archive::new(GzDecoder::new(file)); let prefix = \"bundle/logs\"; println!(\"Extracted the following files:\"); archive .entries()? // 获取压缩档案中的文件条目列表 .filter_map(|e| e.ok()) // 对每个文件条目进行 map 处理 .map(|mut entry| -> Result { // 将文件路径名中的前缀移除,获取一个新的路径名 let path = entry.path()?.strip_prefix(prefix)?.to_owned(); // 将内容解压到新的路径名中 entry.unpack(&path)?; Ok(path) }) .filter_map(|e| e.ok()) .for_each(|x| println!(\"> {}\", x.display())); Ok(())\n}","breadcrumbs":"实用算法 » 压缩算法 » 使用.tar包 » 解压的同时删除指定的文件前缀","id":"158","title":"解压的同时删除指定的文件前缀"},"159":{"body":"","breadcrumbs":"实用算法 » 密码学 » 哈希 » 哈希","id":"159","title":"哈希"},"16":{"body":"Serde 一个超高性能的通用序列化/反序列化框架,可以跟多种协议的库联合使用,实现统一编解码格式 CSV BurntSushi/rust-csv 高性能CSV读写库,支持 Serde JSON serde-rs/json 快到上天的JSON库,也是Rust事实上的标准JSON库,你也可以使用它的大哥 serde ,一个更通用的序列化/反序列化库 MsgPack 3Hren/msgpack-rust 纯Rust实现的MessagePack编解码协议 ProtocolBuffers tokio-rs/prost tokio出品,基本都属精品,此库也不例外,简单易用,文档详细 stepancheg/rust-protobuf 纯Rust实现 TOML alexcrichton/toml-rs TOML编码/解码,可以配合serde使用 XML tafia/quick-xml 高性能XML库,可以配合serde使用,文档较为详细 YAML dtolnay/serde-yaml 使用serde编解码YAML格式的数据","breadcrumbs":"日常开发常用库 » 编解码","id":"16","title":"编解码"},"160":{"body":"写入一些数据到文件中,然后使用 digest::Context 来计算文件内容的 SHA-256 摘要 digest::Digest 。 # use error_chain::error_chain;\nuse data_encoding::HEXUPPER;\nuse ring::digest::{Context, Digest, SHA256};\nuse std::fs::File;\nuse std::io::{BufReader, Read, Write}; # error_chain! {\n# foreign_links {\n# Io(std::io::Error);\n# Decode(data_encoding::DecodeError);\n# }\n# } fn sha256_digest(mut reader: R) -> Result { let mut context = Context::new(&SHA256); let mut buffer = [0; 1024]; loop { let count = reader.read(&mut buffer)?; if count == 0 { break; } context.update(&buffer[..count]); } Ok(context.finish())\n} fn main() -> Result<()> { let path = \"file.txt\"; let mut output = File::create(path)?; write!(output, \"We will generate a digest of this text\")?; let input = File::open(path)?; let reader = BufReader::new(input); let digest = sha256_digest(reader)?; println!(\"SHA-256 digest is {}\", HEXUPPER.encode(digest.as_ref())); Ok(())\n}","breadcrumbs":"实用算法 » 密码学 » 哈希 » 计算文件的 SHA-256 摘要","id":"160","title":"计算文件的 SHA-256 摘要"},"161":{"body":"使用 ring::hmac 创建一个字符串签名并检查该签名的正确性。 use ring::{hmac, rand};\nuse ring::rand::SecureRandom;\nuse ring::error::Unspecified; fn main() -> Result<(), Unspecified> { let mut key_value = [0u8; 48]; let rng = rand::SystemRandom::new(); rng.fill(&mut key_value)?; let key = hmac::Key::new(hmac::HMAC_SHA256, &key_value); let message = \"Legitimate and important message.\"; let signature = hmac::sign(&key, message.as_bytes()); hmac::verify(&key, message.as_bytes(), signature.as_ref())?; Ok(())\n}","breadcrumbs":"实用算法 » 密码学 » 哈希 » 使用 HMAC 摘要来签名和验证消息","id":"161","title":"使用 HMAC 摘要来签名和验证消息"},"162":{"body":"","breadcrumbs":"实用算法 » 密码学 » 加密 » 加密","id":"162","title":"加密"},"163":{"body":"ring::pbkdf2 可以对一个加盐密码进行哈希。 use data_encoding::HEXUPPER;\nuse ring::error::Unspecified;\nuse ring::rand::SecureRandom;\nuse ring::{digest, pbkdf2, rand};\nuse std::num::NonZeroU32; fn main() -> Result<(), Unspecified> { const CREDENTIAL_LEN: usize = digest::SHA512_OUTPUT_LEN; let n_iter = NonZeroU32::new(100_000).unwrap(); let rng = rand::SystemRandom::new(); let mut salt = [0u8; CREDENTIAL_LEN]; // 生成 salt: 将安全生成的随机数填入到字节数组中 rng.fill(&mut salt)?; let password = \"Guess Me If You Can!\"; let mut pbkdf2_hash = [0u8; CREDENTIAL_LEN]; pbkdf2::derive( pbkdf2::PBKDF2_HMAC_SHA512, n_iter, &salt, password.as_bytes(), &mut pbkdf2_hash, ); println!(\"Salt: {}\", HEXUPPER.encode(&salt)); println!(\"PBKDF2 hash: {}\", HEXUPPER.encode(&pbkdf2_hash)); // `verify` 检查哈希是否正确 let should_`succeed = pbkdf2::verify( pbkdf2::PBKDF2_HMAC_SHA512, n_iter, &salt, password.as_bytes(), &pbkdf2_hash, ); let wrong_password = \"Definitely not the correct password\"; let should_fail = pbkdf2::verify( pbkdf2::PBKDF2_HMAC_SHA512, n_iter, &salt, wrong_password.as_bytes(), &pbkdf2_hash, ); assert!(should_succeed.is_ok()); assert!(!should_fail.is_ok()); Ok(())\n}","breadcrumbs":"实用算法 » 密码学 » 加密 » 使用 PBKDF2 对密码进行哈希和加盐( salt )","id":"163","title":"使用 PBKDF2 对密码进行哈希和加盐( salt )"},"164":{"body":"","breadcrumbs":"实用算法 » 数学计算 » 线性代数 » 线性代数","id":"164","title":"线性代数"},"165":{"body":"使用 ndarray::arr2 可以创建二阶矩阵,并计算它们的和。 use ndarray::arr2; fn main() { let a = arr2(&[[1, 2, 3], [4, 5, 6]]); let b = arr2(&[[6, 5, 4], [3, 2, 1]]); // 借用 a 和 b,求和后生成新的矩阵 sum let sum = &a + &b; println!(\"{}\", a); println!(\"+\"); println!(\"{}\", b); println!(\"=\"); println!(\"{}\", sum);\n}","breadcrumbs":"实用算法 » 数学计算 » 线性代数 » 矩阵相加","id":"165","title":"矩阵相加"},"166":{"body":"ndarray::ArrayBase::dot 可以用于计算矩阵乘法。 use ndarray::arr2; fn main() { let a = arr2(&[[1, 2, 3], [4, 5, 6]]); let b = arr2(&[[6, 3], [5, 2], [4, 1]]); println!(\"{}\", a.dot(&b));\n}","breadcrumbs":"实用算法 » 数学计算 » 线性代数 » 矩阵相乘","id":"166","title":"矩阵相乘"},"167":{"body":"在 ndarry中,1 阶数组根据上下文既可以作为行向量也可以作为列向量。如果对你来说,这个行或列的方向很重要,可以考虑使用一行或一列的 2 阶数组来表示。 在下面例子中,由于 1 阶数组处于乘号的右边位置,因此 dot 会把它当成列向量来处理。 use ndarray::{arr1, arr2, Array1}; fn main() { let scalar = 4; let vector = arr1(&[1, 2, 3]); let matrix = arr2(&[[4, 5, 6], [7, 8, 9]]); let new_vector: Array1<_> = scalar * vector; println!(\"{}\", new_vector); let new_matrix = matrix.dot(&new_vector); println!(\"{}\", new_matrix);\n}","breadcrumbs":"实用算法 » 数学计算 » 线性代数 » 标量、向量、矩阵相乘","id":"167","title":"标量、向量、矩阵相乘"},"168":{"body":"浮点数通常是不精确的,因此比较浮点数不是一件简单的事。 approx 提供的 assert_abs_diff_eq! 宏提供了方便的按元素比较的方式。为了使用 approx ,你需要在 ndarray 的依赖中开启相应的 feature:例如,在 Cargo.toml 中修改 ndarray 的依赖引入为 ndarray = { version = \"0.13\", features = [\"approx\"] }。 use approx::assert_abs_diff_eq;\nuse ndarray::Array; fn main() { let a = Array::from(vec![1., 2., 3., 4., 5.]); let b = Array::from(vec![5., 4., 3., 2., 1.]); let mut c = Array::from(vec![1., 2., 3., 4., 5.]); let mut d = Array::from(vec![5., 4., 3., 2., 1.]); // 消耗 a 和 b 的所有权 let z = a + b; // 借用 c 和 d let w = &c + &d; assert_abs_diff_eq!(z, Array::from(vec![6., 6., 6., 6., 6.])); println!(\"c = {}\", c); c[0] = 10.; d[1] = 10.; assert_abs_diff_eq!(w, Array::from(vec![6., 6., 6., 6., 6.])); }","breadcrumbs":"实用算法 » 数学计算 » 线性代数 » 向量比较","id":"168","title":"向量比较"},"169":{"body":"需要注意的是 Array 和 ArrayView 都是 ArrayBase 的别名。因此一个更通用的参数应该是 &ArrayBase where S: Data,特别是在你提供一个公共 API 给其它用户时,但由于咱们是内部使用,因此更精准的 ArrayView1 会更适合。 use ndarray::{array, Array1, ArrayView1}; fn l1_norm(x: ArrayView1) -> f64 { x.fold(0., |acc, elem| acc + elem.abs())\n} fn l2_norm(x: ArrayView1) -> f64 { x.dot(&x).sqrt()\n} fn normalize(mut x: Array1) -> Array1 { let norm = l2_norm(x.view()); x.mapv_inplace(|e| e/norm); x\n} fn main() { let x = array![1., 2., 3., 4., 5.]; println!(\"||x||_2 = {}\", l2_norm(x.view())); println!(\"||x||_1 = {}\", l1_norm(x.view())); println!(\"Normalizing x yields {:?}\", normalize(x));\n}","breadcrumbs":"实用算法 » 数学计算 » 线性代数 » 向量范数( norm )","id":"169","title":"向量范数( norm )"},"17":{"body":"跨平台 DioxusLabs/Dioxus 跨平台 UI 开发框架,支持 WASM、Desktop、TUI 等应用开发,文档较为详细","breadcrumbs":"日常开发常用库 » UI 开发框架","id":"17","title":"UI 开发框架"},"170":{"body":"例子中使用 nalgebra::Matrix3 创建一个 3x3 的矩阵,然后尝试对其进行逆变换,获取一个逆矩阵。 use nalgebra::Matrix3; fn main() { let m1 = Matrix3::new(2.0, 1.0, 1.0, 3.0, 2.0, 1.0, 2.0, 1.0, 2.0); println!(\"m1 = {}\", m1); match m1.try_inverse() { Some(inv) => { println!(\"The inverse of m1 is: {}\", inv); } None => { println!(\"m1 is not invertible!\"); } }\n}","breadcrumbs":"实用算法 » 数学计算 » 线性代数 » 矩阵的逆变换","id":"170","title":"矩阵的逆变换"},"171":{"body":"下面将展示如何将矩阵序列化为 JSON ,然后再反序列化为原矩阵。 extern crate nalgebra;\nextern crate serde_json; use nalgebra::DMatrix; fn main() -> Result<(), std::io::Error> { let row_slice: Vec = (1..5001).collect(); let matrix = DMatrix::from_row_slice(50, 100, &row_slice); // 序列化矩阵 let serialized_matrix = serde_json::to_string(&matrix)?; // 反序列化 let deserialized_matrix: DMatrix = serde_json::from_str(&serialized_matrix)?; // 验证反序列化后的矩阵跟原始矩阵相等 assert!(deserialized_matrix == matrix); Ok(())\n}","breadcrumbs":"实用算法 » 数学计算 » 线性代数 » 序列/反序列化一个矩阵","id":"171","title":"序列/反序列化一个矩阵"},"172":{"body":"","breadcrumbs":"实用算法 » 数学计算 » 三角函数 » 三角函数","id":"172","title":"三角函数"},"173":{"body":"计算角为 2 弧度、对边长度为 80 的直角三角形的斜边长度。 fn main() { let angle: f64 = 2.0; let side_length = 80.0; let hypotenuse = side_length / angle.sin(); println!(\"Hypotenuse: {}\", hypotenuse);\n}","breadcrumbs":"实用算法 » 数学计算 » 三角函数 » 三角形边长计算","id":"173","title":"三角形边长计算"},"174":{"body":"fn main() { let x: f64 = 6.0; let a = x.tan(); let b = x.sin() / x.cos(); assert_eq!(a, b);\n}","breadcrumbs":"实用算法 » 数学计算 » 三角函数 » 验证 tan = sin / cos","id":"174","title":"验证 tan = sin / cos"},"175":{"body":"下面的代码使用 Haversine 公式 计算地球上两点之间的公里数。 fn main() { let earth_radius_kilometer = 6371.0_f64; let (paris_latitude_degrees, paris_longitude_degrees) = (48.85341_f64, -2.34880_f64); let (london_latitude_degrees, london_longitude_degrees) = (51.50853_f64, -0.12574_f64); let paris_latitude = paris_latitude_degrees.to_radians(); let london_latitude = london_latitude_degrees.to_radians(); let delta_latitude = (paris_latitude_degrees - london_latitude_degrees).to_radians(); let delta_longitude = (paris_longitude_degrees - london_longitude_degrees).to_radians(); let central_angle_inner = (delta_latitude / 2.0).sin().powi(2) + paris_latitude.cos() * london_latitude.cos() * (delta_longitude / 2.0).sin().powi(2); let central_angle = 2.0 * central_angle_inner.sqrt().asin(); let distance = earth_radius_kilometer * central_angle; println!( \"Distance between Paris and London on the surface of Earth is {:.1} kilometers\", distance );\n}","breadcrumbs":"实用算法 » 数学计算 » 三角函数 » 地球上两点间的距离","id":"175","title":"地球上两点间的距离"},"176":{"body":"","breadcrumbs":"实用算法 » 数学计算 » 复数 » 复数","id":"176","title":"复数"},"177":{"body":"num::complex::Complex 可以帮助我们创建复数,其中实部和虚部必须是一样的类型。 fn main() { let complex_integer = num::complex::Complex::new(10, 20); let complex_float = num::complex::Complex::new(10.1, 20.1); println!(\"Complex integer: {}\", complex_integer); println!(\"Complex float: {}\", complex_float);\n}","breadcrumbs":"实用算法 » 数学计算 » 复数 » 创建复数","id":"177","title":"创建复数"},"178":{"body":"复数计算和 Rust 基本类型的计算并无区别。 fn main() { let complex_num1 = num::complex::Complex::new(10.0, 20.0); // Must use floats let complex_num2 = num::complex::Complex::new(3.1, -4.2); let sum = complex_num1 + complex_num2; println!(\"Sum: {}\", sum);\n}","breadcrumbs":"实用算法 » 数学计算 » 复数 » 复数相加","id":"178","title":"复数相加"},"179":{"body":"在 num::complex::Complex 中定义了一些内置的数学函数,可用于对复数进行数学运算。 use std::f64::consts::PI;\nuse num::complex::Complex; fn main() { let x = Complex::new(0.0, 2.0*PI); println!(\"e^(2i * pi) = {}\", x.exp()); // =~1\n}","breadcrumbs":"实用算法 » 数学计算 » 复数 » 数学函数","id":"179","title":"数学函数"},"18":{"body":"lettre/lettre — Rust SMTP库","breadcrumbs":"日常开发常用库 » Email","id":"18","title":"Email"},"180":{"body":"","breadcrumbs":"实用算法 » 数学计算 » 统计学 » 统计","id":"180","title":"统计"},"181":{"body":"下面的一些例子为 Rust 数组中的数据计算它们的中心趋势。 平均值 首先计算的是平均值。 fn main() { let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11]; let sum = data.iter().sum::() as f32; let count = data.len(); let mean = match count { positive if positive > 0 => Some(sum / count as f32), _ => None }; println!(\"Mean of the data is {:?}\", mean);\n} 中位数 下面使用快速选择算法来计算中位数。该算法只会对可能包含中位数的数据分区进行排序,从而避免了对所有数据进行全排序。 use std::cmp::Ordering; fn partition(data: &[i32]) -> Option<(Vec, i32, Vec)> { match data.len() { 0 => None, _ => { let (pivot_slice, tail) = data.split_at(1); let pivot = pivot_slice[0]; let (left, right) = tail.iter() .fold((vec![], vec![]), |mut splits, next| { { let (ref mut left, ref mut right) = &mut splits; if next < &pivot { left.push(*next); } else { right.push(*next); } } splits }); Some((left, pivot, right)) } }\n} fn select(data: &[i32], k: usize) -> Option { let part = partition(data); match part { None => None, Some((left, pivot, right)) => { let pivot_idx = left.len(); match pivot_idx.cmp(&k) { Ordering::Equal => Some(pivot), Ordering::Greater => select(&left, k), Ordering::Less => select(&right, k - (pivot_idx + 1)), } }, }\n} fn median(data: &[i32]) -> Option { let size = data.len(); match size { even if even % 2 == 0 => { let fst_med = select(data, (even / 2) - 1); let snd_med = select(data, even / 2); match (fst_med, snd_med) { (Some(fst), Some(snd)) => Some((fst + snd) as f32 / 2.0), _ => None } }, odd => select(data, odd / 2).map(|x| x as f32) }\n} fn main() { let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11]; let part = partition(&data); println!(\"Partition is {:?}\", part); let sel = select(&data, 5); println!(\"Selection at ordered index {} is {:?}\", 5, sel); let med = median(&data); println!(\"Median is {:?}\", med);\n} 众数( mode ) 下面使用了 HashMap 对不同数字出现的次数进行了分别统计。 use std::collections::HashMap; fn main() { let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11]; let frequencies = data.iter().fold(HashMap::new(), |mut freqs, value| { *freqs.entry(value).or_insert(0) += 1; freqs }); let mode = frequencies .into_iter() .max_by_key(|&(_, count)| count) .map(|(value, _)| *value); println!(\"Mode of the data is {:?}\", mode);\n}","breadcrumbs":"实用算法 » 数学计算 » 统计学 » 测量中心趋势","id":"181","title":"测量中心趋势"},"182":{"body":"下面一起来看看该如何计算一组测量值的标准偏差和 z-score。 fn mean(data: &[i32]) -> Option { let sum = data.iter().sum::() as f32; let count = data.len(); match count { positive if positive > 0 => Some(sum / count as f32), _ => None, }\n} fn std_deviation(data: &[i32]) -> Option { match (mean(data), data.len()) { (Some(data_mean), count) if count > 0 => { let variance = data.iter().map(|value| { let diff = data_mean - (*value as f32); diff * diff }).sum::() / count as f32; Some(variance.sqrt()) }, _ => None }\n} fn main() { let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11]; let data_mean = mean(&data); println!(\"Mean is {:?}\", data_mean); let data_std_deviation = std_deviation(&data); println!(\"Standard deviation is {:?}\", data_std_deviation); let zscore = match (data_mean, data_std_deviation) { (Some(mean), Some(std_deviation)) => { let diff = data[4] as f32 - mean; Some(diff / std_deviation) }, _ => None }; println!(\"Z-score of data at index 4 (with value {}) is {:?}\", data[4], zscore);\n}","breadcrumbs":"实用算法 » 数学计算 » 统计学 » 标准偏差","id":"182","title":"标准偏差"},"183":{"body":"","breadcrumbs":"实用算法 » 数学计算 » 杂项 » 杂项","id":"183","title":"杂项"},"184":{"body":"使用 BitInt 可以对超过 128bit 的整数进行计算。 use num::bigint::{BigInt, ToBigInt}; fn factorial(x: i32) -> BigInt { if let Some(mut factorial) = 1.to_bigint() { for i in 1..=x { factorial = factorial * i; } factorial } else { panic!(\"Failed to calculate factorial!\"); }\n} fn main() { println!(\"{}! equals {}\", 100, factorial(100));\n}","breadcrumbs":"实用算法 » 数学计算 » 杂项 » 大整数 Big int","id":"184","title":"大整数 Big int"},"185":{"body":"","breadcrumbs":"数据结构 » 数据结构","id":"185","title":"数据结构"},"186":{"body":"","breadcrumbs":"数据结构 » 位字段 » 位字段","id":"186","title":"位字段"},"187":{"body":"使用 bitflags! 宏可以帮助我们创建安全的位字段类型 MyFlags,然后为其实现基本的 clear 操作。以下代码展示了基本的位操作和格式化: use bitflags::bitflags;\nuse std::fmt; bitflags! { struct MyFlags: u32 { const FLAG_A = 0b00000001; const FLAG_B = 0b00000010; const FLAG_C = 0b00000100; const FLAG_ABC = Self::FLAG_A.bits | Self::FLAG_B.bits | Self::FLAG_C.bits; }\n} impl MyFlags { pub fn clear(&mut self) -> &mut MyFlags { self.bits = 0; self }\n} impl fmt::Display for MyFlags { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, \"{:032b}\", self.bits) }\n} fn main() { let e1 = MyFlags::FLAG_A | MyFlags::FLAG_C; let e2 = MyFlags::FLAG_B | MyFlags::FLAG_C; assert_eq!((e1 | e2), MyFlags::FLAG_ABC); assert_eq!((e1 & e2), MyFlags::FLAG_C); assert_eq!((e1 - e2), MyFlags::FLAG_A); assert_eq!(!e2, MyFlags::FLAG_A); let mut flags = MyFlags::FLAG_ABC; assert_eq!(format!(\"{}\", flags), \"00000000000000000000000000000111\"); assert_eq!(format!(\"{}\", flags.clear()), \"00000000000000000000000000000000\"); assert_eq!(format!(\"{:?}\", MyFlags::FLAG_B), \"FLAG_B\"); assert_eq!(format!(\"{:?}\", MyFlags::FLAG_A | MyFlags::FLAG_B), \"FLAG_A | FLAG_B\");\n}","breadcrumbs":"数据结构 » 位字段 » 定义和操作位字段","id":"187","title":"定义和操作位字段"},"188":{"body":"对于每一个程序员而言,命令行工具都非常关键。你对他越熟悉,在使用计算机、处理工作流程等越是高效。 下面我们收集了一些优秀的Rust所写的命令行工具,它们相比目前已有的其它语言的实现,可以提供更加现代化的代码实现、更加高效的性能以及更好的可用性。","breadcrumbs":"命令行 » 命令行工具","id":"188","title":"命令行工具"},"189":{"body":"新工具 替代的目标或功能描述 bat cat exa ls lsd ls fd find procs ps sd sed dust du starship 现代化的命令行提示 ripgrep grep tokei 代码统计工具 hyperfine 命令行benchmark工具 bottom top teeldear tldr grex 根据文本示例生成正则 bandwitch 显示进程、连接网络使用情况 zoxide cd delta git可视化 nushell 全新的现代化shell mcfly 替代ctrl + R命令搜索 fselect 使用SQL语法查找文件 pueue 命令行任务管理工具 watchexec 监视目录文件变动并执行命令 dura 更加安全的使用git alacritty 强大的基于OpenGL的终端 broot 可视化访问目录树","breadcrumbs":"命令行 » 索引目录","id":"189","title":"索引目录"},"19":{"body":"","breadcrumbs":"日常开发常用库 » 常用正则模版","id":"19","title":"常用正则模版"},"190":{"body":"bat 克隆了**cat**的功能并提供了语法高亮和Git集成,它支持Windows,MacOS和Linux`。同时,它默认提供了多种文件后缀的语法高亮。","breadcrumbs":"命令行 » bat","id":"190","title":"bat"},"191":{"body":"exa 是ls命令的现代化实现,后者是目前Unix/Linux系统的默认命令,用于列出当前目录中的内容。","breadcrumbs":"命令行 » exa","id":"191","title":"exa"},"192":{"body":"lsd 也是 ls 的新实现,同时增加了很多特性,例如:颜色标注、icons、树形查看、更多的格式化选项等。","breadcrumbs":"命令行 » lsd","id":"192","title":"lsd"},"193":{"body":"fd 是一个更快、对用户更友好的 find 实现,后者是 Unix/Linux 内置的文件目录搜索工具。之所以说它用户友好,一方面是 API 非常清晰明了,其次是它对最常用的场景提供了有意义的默认值:例如,想要通过名称搜索文件: fd: fd PATTERN find: find -iname 'PATTERN' 同时 fd 性能非常非常高,还提供了非常多的搜索选项,例如允许用户通过 .gitignore 文件忽略隐藏的目录、文件等。","breadcrumbs":"命令行 » fd","id":"193","title":"fd"},"194":{"body":"procs 是 ps 的默认实现,后者是 Unix/Linux 的内置命令,用于获取进程( process )的信息。proc 提供了更便利、可读性更好的格式化输出。","breadcrumbs":"命令行 » procs","id":"194","title":"procs"},"195":{"body":"sd 是 sed 命令的现代化实现,后者是 Unix/Linux 中内置的工具,用于分析和转换文本。 sd 拥有更简单的使用方式,而且支持方便的正则表达式语法,sd 拥有闪电般的性能,比 sed 快 2x-11x 倍。 以下是其中一个性能测试结果: 对1.5G大小的 JSON 文本进行简单替换 hyperfine -w 3 'sed -E \"s/\\\"/\\'/g\" *.json >/dev/null' 'sd \"\\\"\" \"\\'\" *.json >/dev/null' --export-markdown out.md Command Mean [s] Min…Max [s] sed -E \"s/\\\"/'/g\" *.json >/dev/null 2.338 ± 0.008 2.332…2.358 sed \"s/\\\"/'/g\" *.json >/dev/null 2.365 ± 0.009 2.351…2.378 sd \"\\\"\" \"'\" *.json >/dev/null 0.997 ± 0.006 0.987…1.007 结果: ~2.35 times faster","breadcrumbs":"命令行 » sd","id":"195","title":"sd"},"196":{"body":"dust 是一个更符合使用习惯的 du ,后者是 Unix/Linux 内置的命令行工具,用于显示硬盘使用情况的统计。","breadcrumbs":"命令行 » dust","id":"196","title":"dust"},"197":{"body":"starship 是一个命令行提示,支持任何 shell ,包括 zsh ,简单易用、非常快且拥有极高的可配置性, 同时支持智能提示。","breadcrumbs":"命令行 » starship","id":"197","title":"starship"},"198":{"body":"ripgrep 是一个性能极高的现代化 grep 实现,后者是 Unix/Linux 下的内置文件搜索工具。该项目是 Rust 的明星项目,一个是因为性能极其的高,另一个就是源代码质量很高,值得学习, 同时 Vscode 使用它作为内置的搜索引擎。 从功能来说,除了全面支持 grep 的功能外,repgre 支持使用正则递归搜索指定的文件目录,默认使用 .gitignore 对指定的文件进行忽略。","breadcrumbs":"命令行 » ripgrep","id":"198","title":"ripgrep"},"199":{"body":"tokei 可以分门别类的统计目录内的代码行数,速度非常快!","breadcrumbs":"命令行 » tokei","id":"199","title":"tokei"},"2":{"body":"本书适合所有程度的 Rust 开发者使用: 新手用来了解 Rust 的常用库和常用代码片段 老手在写代码时,可以直接用来复制粘贴,大幅提升工作效率 毕竟咱不是在面试造飞机,谁脑袋中能记住文件操作的各种细节,对不?","breadcrumbs":"Rusty Book » 这本书的读者","id":"2","title":"这本书的读者"},"20":{"body":"滚滚长江东逝水,浪花淘尽英雄,是非成败转头空 - 临江仙·滚滚长江东逝水 经过大浪淘沙留下来的才是真金白银,对于开源项目也是如此。对于明星项目,本文不仅仅以star数的多少作为评判维度,还会结合项目规模、影响力、活跃度、社区活跃度等多个方面进行评定,希望大家能喜欢。 需要注意,本文列出的几乎都是平台级项目,因此并不是star多,就能名列其中,例如很多star很多的工具、Rust库、书籍都没有列入,如果大家想要看更多的子类项目,请访问对应的文件进行查看。","breadcrumbs":"Rust 明星项目 » 明星项目","id":"20","title":"明星项目"},"200":{"body":"hyperfine 是命令行benchmark工具,它支持在多次运行中提供静态的分析,同时支持任何的 shell 命令,准确的 benchmark 进度和当前预估等等高级特性。","breadcrumbs":"命令行 » hyperfine","id":"200","title":"hyperfine"},"201":{"body":"bottom 是一个现代化实现的 top,可以跨平台、图形化的显示进程/系统的当前信息。","breadcrumbs":"命令行 » bottom","id":"201","title":"bottom"},"202":{"body":"tealdear 是一个更快实现的 tldr , 一个用于显示 man pages 的命令行程序,简单易用、基于例子和社区驱动是主要特性。","breadcrumbs":"命令行 » tealdear","id":"202","title":"tealdear"},"203":{"body":"bandwhich 是一个客户端实用工具,用于显示当前进程、连接、远程 IP( hostname ) 的网络信息。","breadcrumbs":"命令行 » bandwhich","id":"203","title":"bandwhich"},"204":{"body":"grex 既是一个命令行工具又是一个库,可以根据用户提供的文本示例生成对应的正则表达式,非常强大。","breadcrumbs":"命令行 » grex","id":"204","title":"grex"},"205":{"body":"zoxide 是一个智能化的 cd 命令,它甚至会记忆你常用的目录。","breadcrumbs":"命令行 » zoxide","id":"205","title":"zoxide"},"206":{"body":"delta 是一个 git 分页展示工具,支持语法高亮、代码比对、输出 grep 等。","breadcrumbs":"命令行 » delta","id":"206","title":"delta"},"207":{"body":"nushell 是一个全新的 shell ,使用 Rust 实现。它的目标是创建一个现代化的 shell :虽然依然基于 Unix 的哲学,但是更适合现在的时代。例如,你可以使用 SQL 语法来选择你想要的内容!","breadcrumbs":"命令行 » nushell","id":"207","title":"nushell"},"208":{"body":"mcfly 会替换默认的 ctrl-R,用于在终端中搜索历史命令, 它提供了智能提示功能,并且会根据当前目录中最近执行过的上下文命令进行提示。mcfly 甚至使用了一个小型的神经网络用于智能提示!","breadcrumbs":"命令行 » mcfly","id":"208","title":"mcfly"},"209":{"body":"fselect 允许使用 SQL 语法来查找系统中的文件。它支持复杂查询、聚合查询、.gitignore 忽略文件、通过宽度高度搜索图片、通过 hash 搜索文件、文件属性查询等等,相当强大! # 复杂查询\nfselect \"name from /tmp where (name = *.tmp and size = 0) or (name = *.cfg and size > 1000000)\" # 聚合函数\nfselect \"MIN(size), MAX(size), AVG(size), SUM(size), COUNT(*) from /home/user/Downloads\" # 格式化函数\nfselect \"LOWER(name), UPPER(name), LENGTH(name), YEAR(modified) from /home/user/Downloads\"","breadcrumbs":"命令行 » fselect","id":"209","title":"fselect"},"21":{"body":"首先出场的自然是咖位最重的之一,可以说正是因为deno和swc的横空出世,才让一堆观望的大神对于Rust实现Javascript基建有了更强的信心。 deno 是node半逆转后的字序,从此可以看出deno是Node.js的替代,它的目标是为Typescript/Javascript提供一个更现代化、更安全、更强大 的运行时,同时内置了很多强大的工具,可以用于打包、编译成可执行文件、文档、测试、lint等。","breadcrumbs":"Rust 明星项目 » deno","id":"21","title":"deno"},"210":{"body":"pueue 是一个命令行任务管理工具,它可以管理你的长时间运行的命令,支持顺序或并行执行。简单来说,它可以管理一个命令队列。","breadcrumbs":"命令行 » pueue","id":"210","title":"pueue"},"211":{"body":"watchexec 可以监视指定的目录、文件的改动,并执行你预设的命令,支持多种配置项和操作系统。 # 监视当前目录/子目录中的所有js、css、html文件,一旦发生改变,运行`npm run build`命令\n$ watchexec -e js,css,html npm run build # 当前目录/子目录下任何python文件发生改变时,重启`python server.py`\n$ watchexec -r -e py -- python server.py","breadcrumbs":"命令行 » watchexec","id":"211","title":"watchexec"},"212":{"body":"dura 运行在后台,监视你的 git 目录,提交你未提交的更改但是并不会影响 HEAD、当前的分支和 git 索引(staged文件)。 如果你曾经遇到过**\"完蛋, 我这几天的工作内容丢了\"**的情况,那么就可以尝试下 dura,checkout dura brach,然后代码就可以顺利恢复了:) 恢复代码 你可以使用 dura 分支来恢复 $ echo \"dura/$(git rev-parse HEAD)\" 也可以手动恢复 # Or, if you don't trust dura yet, `git stash`\n$ git reset HEAD --hard\n# get the changes into your working directory\n$ git checkout $THE_HASH\n# last few commands reset HEAD back to master but with changes uncommitted\n$ git checkout -b temp-branch\n$ git reset master\n$ git checkout master\n$ git branch -D temp-branch","breadcrumbs":"命令行 » dura","id":"212","title":"dura"},"213":{"body":"alacritty 是一个跨平台、基于OpenGL的终端,性能极高的同时还支持丰富的自定义和可扩展性,可以说是非常优秀的现代化终端。 目前已经是 beta 阶段,可以作为日常工具来使用。","breadcrumbs":"命令行 » alacritty","id":"213","title":"alacritty"},"214":{"body":"broot 允许你可视化的去访问一个目录结构。","breadcrumbs":"命令行 » broot","id":"214","title":"broot"},"215":{"body":"","breadcrumbs":"命令行 » 参数解析 » 参数解析","id":"215","title":"参数解析"},"216":{"body":"下面的程序给出了使用 clap 来解析命令行参数的样式结构,如果大家想了解更多,在 clap 文档 中还给出了另外两种初始化一个应用的方式。 在下面的构建中,value_of 将获取通过 with_name 解析出的值。short 和 long 用于设置用户输入的长短命令格式,例如短命令 -f 和长命令 --file。 use clap::{Arg, App}; fn main() { let matches = App::new(\"My Test Program\") .version(\"0.1.0\") .author(\"Hackerman Jones \") .about(\"Teaches argument parsing\") .arg(Arg::with_name(\"file\") .short(\"f\") .long(\"file\") .takes_value(true) .help(\"A cool file\")) .arg(Arg::with_name(\"num\") .short(\"n\") .long(\"number\") .takes_value(true) .help(\"Five less than your favorite number\")) .get_matches(); let myfile = matches.value_of(\"file\").unwrap_or(\"input.txt\"); println!(\"The file passed is: {}\", myfile); let num_str = matches.value_of(\"num\"); match num_str { None => println!(\"No idea what your favorite number is.\"), Some(s) => { match s.parse::() { Ok(n) => println!(\"Your favorite number must be {}.\", n + 5), Err(_) => println!(\"That's not a number! {}\", s), } } }\n} clap 针对上面提供的构建样式,会自动帮我们生成相应的使用方式说明。例如,上面代码生成的使用说明如下: My Test Program 0.1.0\nHackerman Jones \nTeaches argument parsing USAGE: testing [OPTIONS] FLAGS: -h, --help Prints help information -V, --version Prints version information OPTIONS: -f, --file A cool file -n, --number Five less than your favorite number 最后,再使用一些参数来运行下我们的代码: $ cargo run -- -f myfile.txt -n 251\nThe file passed is: myfile.txt\nYour favorite number must be 256.","breadcrumbs":"命令行 » 参数解析 » Clap","id":"216","title":"Clap"},"217":{"body":"@todo","breadcrumbs":"命令行 » 参数解析 » Structopt","id":"217","title":"Structopt"},"218":{"body":"ansi_term 包可以帮我们控制终端上的输出样式,例如使用颜色文字、控制输出格式等,当然,前提是在 ANSI 终端上。 ansi_term 中有两个主要数据结构: ANSIString 和 Style 。 Style 用于控制样式:颜色、加粗、闪烁等,而前者是一个带有样式的字符串。","breadcrumbs":"命令行 » 终端输出格式化 » ANSI 终端","id":"218","title":"ANSI 终端"},"219":{"body":"use ansi_term::Colour; fn main() { println!(\"This is {} in color, {} in color and {} in color\", Colour::Red.paint(\"red\"), Colour::Blue.paint(\"blue\"), Colour::Green.paint(\"green\"));\n}","breadcrumbs":"命令行 » 终端输出格式化 » 颜色字体","id":"219","title":"颜色字体"},"22":{"body":"alacritty 是一个跨平台、基于OpenGL的终端,性能极高的同时还支持丰富的自定义和可扩展性,可以说是非常优秀的现代化终端。 目前已经是beta阶段,可以作为日常工具来使用。","breadcrumbs":"Rust 明星项目 » alacritty","id":"22","title":"alacritty"},"220":{"body":"比颜色复杂的样式构建需要使用 Style 结构体: use ansi_term::Style; fn main() { println!(\"{} and this is not\", Style::new().bold().paint(\"This is Bold\"));\n}","breadcrumbs":"命令行 » 终端输出格式化 » 加粗字体","id":"220","title":"加粗字体"},"221":{"body":"Colour 实现了很多跟 Style 类似的函数,因此可以实现链式调用。 use ansi_term::Colour;\nuse ansi_term::Style; fn main(){ println!(\"{}, {} and {}\", Colour::Yellow.paint(\"This is colored\"), Style::new().bold().paint(\"this is bold\"), // Colour 也可以使用 bold 方法进行加粗 Colour::Yellow.bold().paint(\"this is bold and colored\"));\n}","breadcrumbs":"命令行 » 终端输出格式化 » 加粗和颜色","id":"221","title":"加粗和颜色"},"222":{"body":"操作系统范畴很大,本章节中精选的内容聚焦在用Rust实现的操作系统以及用Rust写操作系统的教程。","breadcrumbs":"操作系统 » 操作系统","id":"222","title":"操作系统"},"223":{"body":"系统 描述 redox Unix风格的微内核OS tock 嵌入式操作系统 theseus 独特设计的OS writing os in rust 使用Rust开发简单的操作系统 rust-raspberrypi-OS-tutorials Rust嵌入式系统开发教程 rcore-os 清华大学提供的rcore操作系统教程 edu-os 亚琛工业大学操作系统课程的配套项目","breadcrumbs":"操作系统 » 目录","id":"223","title":"目录"},"224":{"body":"redox 是一个 Unix 风格的微内核操作系统,使用 Rust 实现。redox 的目标是安全、快速、免费、可用,它在内核设计上借鉴了很多优秀的内核,例如:SeL4, MINIX, Plan 9和BSD。 但 redox 不仅仅是一个内核,它还是一个功能齐全的操作系统,提供了操作系统该有的功能,例如:内存分配器、文件系统、显示管理、核心工具等等。你可以大概认为它是一个 GNU 或 BSD 生态,但是是通过一门现代化、内存安全的语言实现的。 不过据我仔细观察,redox目前的开发进度不是很活跃,不知道发生了什么,未来若有新的发现会在这里进行更新 - Sunface","breadcrumbs":"操作系统 » redox","id":"224","title":"redox"},"225":{"body":"tock 是一个嵌入式操作系统,设计用于在低内存和低功耗的微控制器上运行多个并发的、相互不信任的应用程序,例如它可在 Cortex-M 和 RISC-V 平台上运行。 Tock 使用两个核心机制保护操作系统中不同组件的安全运行: 内核和设备驱动全部使用Rust编写,提供了很好安全性的同时,还将内核和设备进行了隔离 使用了内存保护单元技术,让应用之间、应用和内核之间实现了安全隔离 具体可通过这本书了解: The Tock Book .","breadcrumbs":"操作系统 » tock","id":"225","title":"tock"},"226":{"body":"Theseus 是从零开始构建的操作系统,完全使用Rust进行开发。它使用了新的操作系统结构、更好的状态管理,以及利用语言内设计原则将操作系统的职责(如资源管理)转移到编译器中。 该OS目前尚处于早期阶段,但是看上去作者很有信心未来可以落地,如果想要了解,可以通过官方提供的 在线书籍 进行学习。","breadcrumbs":"操作系统 » Theseus","id":"226","title":"Theseus"},"227":{"body":"Writing an OS in Rust 是非常有名的博客系列,专门讲解如何使用Rust来写一个简单的操作系统,配套源码在 这里 ,目前已经发布了第二版。 以下是async/await的目录截图:","breadcrumbs":"操作系统 » Writing an OS in Rust","id":"227","title":"Writing an OS in Rust"},"228":{"body":"rust-raspberrypi-OS-tutorials 教大家如何用Rust开发一个嵌入式操作系统,可以运行在树莓派上。这个教程讲得很细,号称手把手教学,而且是从零实现,因此很值得学习。","breadcrumbs":"操作系统 » rust-raspberrypi-OS-tutorials","id":"228","title":"rust-raspberrypi-OS-tutorials"},"229":{"body":"rcore-os 是由清华大学开发的操作系统,用 Rus t实现, 与 linux 相兼容,主要目的目前还是用于教学,因为还有相关的配套教程,非常值得学习。目前支持的功能不完全列表如下:linux 兼容的 syscall 接口、网络协议栈、简单的文件系统、信号系统、异步IO、内核模块化。 内核实现 配套教程 以下是在树莓派上运行的图:","breadcrumbs":"操作系统 » rcore-os","id":"229","title":"rcore-os"},"23":{"body":"starship 是一个命令行提示,支持任何shell,包括zsh,简单易用、非常快且拥有极高的可配置性, 同时支持智能提示。","breadcrumbs":"Rust 明星项目 » starship","id":"23","title":"starship"},"230":{"body":"edu-os 是 Unix 风格的操作系统,用于教学目的,它是亚琛工业大学(RWTH Aachen University)操作系统课程的配套大项目,但是我并没有找到对应的课程资料,根据作者的描述,上面部分的 Writing an OS in Rust 对他有很大的启发。","breadcrumbs":"操作系统 » edu-os","id":"230","title":"edu-os"},"231":{"body":"","breadcrumbs":"操作系统 » 处理器 » 处理器","id":"231","title":"处理器"},"232":{"body":"num_cpus 可以用于获取逻辑和物理的 CPU 核心数,下面的例子是获取逻辑核心数。 fn main() { println!(\"Number of logical cores is {}\", num_cpus::get());\n}","breadcrumbs":"操作系统 » 处理器 » 获取逻辑CPU的核心数","id":"232","title":"获取逻辑CPU的核心数"},"233":{"body":"","breadcrumbs":"操作系统 » 调用系统命令 » 调用系统命令","id":"233","title":"调用系统命令"},"234":{"body":"下面的代码将调用操作系统中的 git log --oneline 命令,然后使用 regex 对它输出到 stdout 上的调用结果进行解析,以获取哈希值和最后 5 条提交信息( commit )。 #use error_chain::error_chain; use std::process::Command;\nuse regex::Regex; #error_chain!{\n# foreign_links {\n# Io(std::io::Error);\n# Regex(regex::Error);\n# Utf8(std::string::FromUtf8Error);\n# }\n#} #[derive(PartialEq, Default, Clone, Debug)]\nstruct Commit { hash: String, message: String,\n} fn main() -> Result<()> { let output = Command::new(\"git\").arg(\"log\").arg(\"--oneline\").output()?; if !output.status.success() { error_chain::bail!(\"Command executed with failing error code\"); } let pattern = Regex::new(r\"(?x) ([0-9a-fA-F]+) # commit hash (.*) # The commit message\")?; String::from_utf8(output.stdout)? .lines() .filter_map(|line| pattern.captures(line)) .map(|cap| { Commit { hash: cap[1].to_string(), message: cap[2].trim().to_string(), } }) .take(5) .for_each(|x| println!(\"{:?}\", x)); Ok(())\n}","breadcrumbs":"操作系统 » 调用系统命令 » 调用一个外部命令并处理输出内容","id":"234","title":"调用一个外部命令并处理输出内容"},"235":{"body":"#use error_chain::error_chain; use std::collections::HashSet;\nuse std::io::Write;\nuse std::process::{Command, Stdio}; #error_chain!{\n# errors { CmdError }\n# foreign_links {\n# Io(std::io::Error);\n# Utf8(std::string::FromUtf8Error);\n# }\n#} fn main() -> Result<()> { let mut child = Command::new(\"python\").stdin(Stdio::piped()) .stderr(Stdio::piped()) .stdout(Stdio::piped()) .spawn()?; child.stdin .as_mut() .ok_or(\"Child process stdin has not been captured!\")? .write_all(b\"import this; copyright(); credits(); exit()\")?; let output = child.wait_with_output()?; if output.status.success() { let raw_output = String::from_utf8(output.stdout)?; let words = raw_output.split_whitespace() .map(|s| s.to_lowercase()) .collect::>(); println!(\"Found {} unique words:\", words.len()); println!(\"{:#?}\", words); Ok(()) } else { let err = String::from_utf8(output.stderr)?; error_chain::bail!(\"External command failed:\\n {}\", err) }\n}","breadcrumbs":"操作系统 » 调用系统命令 » 调用 python 解释器运行代码并检查返回的错误码","id":"235","title":"调用 python 解释器运行代码并检查返回的错误码"},"236":{"body":"下面的例子将显示当前目录中大小排名前十的文件和子目录,效果等效于命令 du -ah . | sort -hr | head -n 10。 Command 命令代表一个进程,其中父进程通过 Stdio::piped 来捕获子进程的输出。 #use error_chain::error_chain; use std::process::{Command, Stdio}; #error_chain! {\n# foreign_links {\n# Io(std::io::Error);\n# Utf8(std::string::FromUtf8Error);\n# }\n#} fn main() -> Result<()> { let directory = std::env::current_dir()?; let mut du_output_child = Command::new(\"du\") .arg(\"-ah\") .arg(&directory) .stdout(Stdio::piped()) .spawn()?; if let Some(du_output) = du_output_child.stdout.take() { let mut sort_output_child = Command::new(\"sort\") .arg(\"-hr\") .stdin(du_output) .stdout(Stdio::piped()) .spawn()?; du_output_child.wait()?; if let Some(sort_output) = sort_output_child.stdout.take() { let head_output_child = Command::new(\"head\") .args(&[\"-n\", \"10\"]) .stdin(sort_output) .stdout(Stdio::piped()) .spawn()?; let head_stdout = head_output_child.wait_with_output()?; sort_output_child.wait()?; println!( \"Top 10 biggest files and directories in '{}':\\n{}\", directory.display(), String::from_utf8(head_stdout.stdout).unwrap() ); } } Ok(())\n}","breadcrumbs":"操作系统 » 调用系统命令 » 通过管道来运行外部命令","id":"236","title":"通过管道来运行外部命令"},"237":{"body":"下面的例子将生成一个子进程,然后将它的标准输出和标准错误输出都输出到同一个文件中。最终的效果跟 Unix 命令 ls . oops >out.txt 2>&1 相同。 File::try_clone 会克隆一份文件句柄的引用,然后保证这两个句柄在写的时候会使用相同的游标位置。 use std::fs::File;\nuse std::io::Error;\nuse std::process::{Command, Stdio}; fn main() -> Result<(), Error> { let outputs = File::create(\"out.txt\")?; let errors = outputs.try_clone()?; Command::new(\"ls\") .args(&[\".\", \"oops\"]) .stdout(Stdio::from(outputs)) .stderr(Stdio::from(errors)) .spawn()? .wait_with_output()?; Ok(())\n}","breadcrumbs":"操作系统 » 调用系统命令 » 将子进程的 stdout 和 stderr 重定向到同一个文件","id":"237","title":"将子进程的 stdout 和 stderr 重定向到同一个文件"},"238":{"body":"下面的代码会创建一个管道,然后当 BufReader 更新时,就持续从 stdout 中读取数据。最终效果等同于 Unix 命令 journalctl | grep usb。 use std::process::{Command, Stdio};\nuse std::io::{BufRead, BufReader, Error, ErrorKind}; fn main() -> Result<(), Error> { let stdout = Command::new(\"journalctl\") .stdout(Stdio::piped()) .spawn()? .stdout .ok_or_else(|| Error::new(ErrorKind::Other,\"Could not capture standard output.\"))?; let reader = BufReader::new(stdout); reader .lines() .filter_map(|line| line.ok()) .filter(|line| line.find(\"usb\").is_some()) .for_each(|line| println!(\"{}\", line)); Ok(())\n}","breadcrumbs":"操作系统 » 调用系统命令 » 持续处理子进程的输出","id":"238","title":"持续处理子进程的输出"},"239":{"body":"使用 std::env::var 可以读取系统中的环境变量。 use std::env;\nuse std::fs;\nuse std::io::Error; fn main() -> Result<(), Error> { // 读取环境变量 `CONFIG` 的值并写入到 `config_path` 中。 // 若 `CONFIG` 环境变量没有设置,则使用一个默认的值 \"/etc/myapp/config\" let config_path = env::var(\"CONFIG\") .unwrap_or(\"/etc/myapp/config\".to_string()); let config: String = fs::read_to_string(config_path)?; println!(\"Config: {}\", config); Ok(())\n}","breadcrumbs":"操作系统 » 调用系统命令 » 读取环境变量","id":"239","title":"读取环境变量"},"24":{"body":"MeiliSearch 是一个搜索平台,但是跟ElasticSearch不同,MeiliSearch并不是通用目的的,它的目标是为终端用户提供边输入边提示的即刻搜索功能,因此是一个轻量级搜索平台,不适用于数据量大时的搜索目的。 总之,如果你需要在网页端或者APP为用户提供一个搜索条,然后支持输入容错、前缀搜索时,就可以使用它。","breadcrumbs":"Rust 明星项目 » MeiliSearch","id":"24","title":"MeiliSearch"},"240":{"body":"","breadcrumbs":"并发 » 线程 » 线程","id":"240","title":"线程"},"241":{"body":"下面例子用到了 crossbeam 包,它提供了非常实用的、用于并发和并行编程的数据结构和函数。 Scope::spawn 会生成一个被限定了作用域的线程,该线程最大的特点就是:它会在传给 crossbeam::scope 的闭包函数返回前先行结束。得益于这个特点,子线程的创建使用就像是本地闭包函数调用,因此生成的线程内部可以使用外部环境中的变量! fn main() { let arr = &[1, 25, -4, 10]; let max = find_max(arr); assert_eq!(max, Some(25));\n} // 将数组分成两个部分,并使用新的线程对它们进行处理\nfn find_max(arr: &[i32]) -> Option { const THRESHOLD: usize = 2; if arr.len() <= THRESHOLD { return arr.iter().cloned().max(); } let mid = arr.len() / 2; let (left, right) = arr.split_at(mid); crossbeam::scope(|s| { let thread_l = s.spawn(|_| find_max(left)); let thread_r = s.spawn(|_| find_max(right)); let max_l = thread_l.join().unwrap()?; let max_r = thread_r.join().unwrap()?; Some(max_l.max(max_r)) }).unwrap()\n}","breadcrumbs":"并发 » 线程 » 生成一个临时性的线程","id":"241","title":"生成一个临时性的线程"},"242":{"body":"下面我们使用 crossbeam 和 crossbeam-channel 来创建一个并行流水线:流水线的两端分别是数据源和数据下沉( sink ),在流水线中间,有两个工作线程会从源头接收数据,对数据进行并行处理,最后将数据下沉。 消息通道( channel )是 crossbeam_channel::bounded ,它只能缓存一条消息。当缓存满后,发送者继续调用 [crossbeam_channel::Sender::send] 发送消息时会阻塞,直到一个工作线程( 消费者 ) 拿走这条消息 消费者获取消息时先到先得的策略,因此两个工作线程只有一个能取到消息,保证消息不会被重复消费、处理 通过迭代器 crossbeam_channel::Receiver::iter 读取消息会阻塞当前线程,直到新消息的到来或 channel 关闭 channel 只有在所有的发送者或消费者关闭后,才能被关闭。而其中一个消费者 rcv2 处于阻塞读取状态,无比被关闭,因此我们必须要关闭所有发送者: drop(snd1); drop(snd2) ,这样 channel 关闭后,主线程的 rcv2 才能从阻塞状态退出,最后整个程序结束。大家还是迷惑的话,可以看看这篇 文章 。 extern crate crossbeam;\nextern crate crossbeam_channel; use std::thread;\nuse std::time::Duration;\nuse crossbeam_channel::bounded; fn main() { let (snd1, rcv1) = bounded(1); let (snd2, rcv2) = bounded(1); let n_msgs = 4; let n_workers = 2; crossbeam::scope(|s| { // 生产者线程 s.spawn(|_| { for i in 0..n_msgs { snd1.send(i).unwrap(); println!(\"Source sent {}\", i); } // 关闭其中一个发送者 snd1 // 该关闭操作对于结束最后的循环是必须的 drop(snd1); }); // 通过两个线程并行处理 for _ in 0..n_workers { // 从数据源接收数据,然后发送到下沉端 let (sendr, recvr) = (snd2.clone(), rcv1.clone()); // 生成单独的工作线程 s.spawn(move |_| { thread::sleep(Duration::from_millis(500)); // 等待通道的关闭 for msg in recvr.iter() { println!(\"Worker {:?} received {}.\", thread::current().id(), msg); sendr.send(msg * 2).unwrap(); } }); } // 关闭通道,如果不关闭,下沉端将永远无法结束循环 drop(snd2); // 下沉端 for msg in rcv2.iter() { println!(\"Sink received {}\", msg); } }).unwrap();\n}","breadcrumbs":"并发 » 线程 » 创建并行流水线","id":"242","title":"创建并行流水线"},"243":{"body":"下面我们来看看 crossbeam-channel 的单生产者单消费者( SPSC ) 使用场景。 use std::{thread, time};\nuse crossbeam_channel::unbounded; fn main() { // unbounded 意味着 channel 可以存储任意多的消息 let (snd, rcv) = unbounded(); let n_msgs = 5; crossbeam::scope(|s| { s.spawn(|_| { for i in 0..n_msgs { snd.send(i).unwrap(); thread::sleep(time::Duration::from_millis(100)); } }); }).unwrap(); for _ in 0..n_msgs { let msg = rcv.recv().unwrap(); println!(\"Received {}\", msg); }\n}","breadcrumbs":"并发 » 线程 » 线程间传递数据","id":"243","title":"线程间传递数据"},"244":{"body":"lazy_static 会创建一个全局的静态引用( static ref ),该引用使用了 Mutex 以支持可变性,因此我们可以在代码中对其进行修改。Mutex 能保证该全局状态同时只能被一个线程所访问。 use error_chain::error_chain;\nuse lazy_static::lazy_static;\nuse std::sync::Mutex; error_chain!{ } lazy_static! { static ref FRUIT: Mutex> = Mutex::new(Vec::new());\n} fn insert(fruit: &str) -> Result<()> { let mut db = FRUIT.lock().map_err(|_| \"Failed to acquire MutexGuard\")?; db.push(fruit.to_string()); Ok(())\n} fn main() -> Result<()> { insert(\"apple\")?; insert(\"orange\")?; insert(\"peach\")?; { let db = FRUIT.lock().map_err(|_| \"Failed to acquire MutexGuard\")?; db.iter().enumerate().for_each(|(i, item)| println!(\"{}: {}\", i, item)); } insert(\"grape\")?; Ok(())\n}","breadcrumbs":"并发 » 线程 » 维护全局可变的状态","id":"244","title":"维护全局可变的状态"},"245":{"body":"下面的示例将为当前目录中的每一个 .iso 文件都计算一个 SHA256 sum。其中线程池中会初始化和 CPU 核心数一致的线程数,其中核心数是通过 num_cpus::get 函数获取。 Walkdir::new 可以遍历当前的目录,然后调用 execute 来执行读操作和 SHA256 哈希计算。 use walkdir::WalkDir;\nuse std::fs::File;\nuse std::io::{BufReader, Read, Error};\nuse std::path::Path;\nuse threadpool::ThreadPool;\nuse std::sync::mpsc::channel;\nuse ring::digest::{Context, Digest, SHA256}; // Verify the iso extension\nfn is_iso(entry: &Path) -> bool { match entry.extension() { Some(e) if e.to_string_lossy().to_lowercase() == \"iso\" => true, _ => false, }\n} fn compute_digest>(filepath: P) -> Result<(Digest, P), Error> { let mut buf_reader = BufReader::new(File::open(&filepath)?); let mut context = Context::new(&SHA256); let mut buffer = [0; 1024]; loop { let count = buf_reader.read(&mut buffer)?; if count == 0 { break; } context.update(&buffer[..count]); } Ok((context.finish(), filepath))\n} fn main() -> Result<(), Error> { let pool = ThreadPool::new(num_cpus::get()); let (tx, rx) = channel(); for entry in WalkDir::new(\"/home/user/Downloads\") .follow_links(true) .into_iter() .filter_map(|e| e.ok()) .filter(|e| !e.path().is_dir() && is_iso(e.path())) { let path = entry.path().to_owned(); let tx = tx.clone(); pool.execute(move || { let digest = compute_digest(path); tx.send(digest).expect(\"Could not send data!\"); }); } drop(tx); for t in rx.iter() { let (sha, path) = t?; println!(\"{:?} {:?}\", sha, path); } Ok(())\n}","breadcrumbs":"并发 » 线程 » 并行计算 iso 文件的 SHA256","id":"245","title":"并行计算 iso 文件的 SHA256"},"246":{"body":"下面例子中将基于 Julia Set 来绘制一个分形图片,其中使用到了线程池来做分布式计算。 # use error_chain::error_chain;\nuse std::sync::mpsc::{channel, RecvError};\nuse threadpool::ThreadPool;\nuse num::complex::Complex;\nuse image::{ImageBuffer, Pixel, Rgb}; #\n# error_chain! {\n# foreign_links {\n# MpscRecv(RecvError);\n# Io(std::io::Error);\n# }\n# }\n#\n# // Function converting intensity values to RGB\n# // Based on http://www.efg2.com/Lab/ScienceAndEngineering/Spectra.htm\n# fn wavelength_to_rgb(wavelength: u32) -> Rgb {\n# let wave = wavelength as f32;\n#\n# let (r, g, b) = match wavelength {\n# 380..=439 => ((440. - wave) / (440. - 380.), 0.0, 1.0),\n# 440..=489 => (0.0, (wave - 440.) / (490. - 440.), 1.0),\n# 490..=509 => (0.0, 1.0, (510. - wave) / (510. - 490.)),\n# 510..=579 => ((wave - 510.) / (580. - 510.), 1.0, 0.0),\n# 580..=644 => (1.0, (645. - wave) / (645. - 580.), 0.0),\n# 645..=780 => (1.0, 0.0, 0.0),\n# _ => (0.0, 0.0, 0.0),\n# };\n#\n# let factor = match wavelength {\n# 380..=419 => 0.3 + 0.7 * (wave - 380.) / (420. - 380.),\n# 701..=780 => 0.3 + 0.7 * (780. - wave) / (780. - 700.),\n# _ => 1.0,\n# };\n#\n# let (r, g, b) = (normalize(r, factor), normalize(g, factor), normalize(b, factor));\n# Rgb::from_channels(r, g, b, 0)\n# }\n#\n# // Maps Julia set distance estimation to intensity values\n# fn julia(c: Complex, x: u32, y: u32, width: u32, height: u32, max_iter: u32) -> u32 {\n# let width = width as f32;\n# let height = height as f32;\n#\n# let mut z = Complex {\n# // scale and translate the point to image coordinates\n# re: 3.0 * (x as f32 - 0.5 * width) / width,\n# im: 2.0 * (y as f32 - 0.5 * height) / height,\n# };\n#\n# let mut i = 0;\n# for t in 0..max_iter {\n# if z.norm() >= 2.0 {\n# break;\n# }\n# z = z * z + c;\n# i = t;\n# }\n# i\n# }\n#\n# // Normalizes color intensity values within RGB range\n# fn normalize(color: f32, factor: f32) -> u8 {\n# ((color * factor).powf(0.8) * 255.) as u8\n# } fn main() -> Result<()> { let (width, height) = (1920, 1080); // 为指定宽高的输出图片分配内存 let mut img = ImageBuffer::new(width, height); let iterations = 300; let c = Complex::new(-0.8, 0.156); let pool = ThreadPool::new(num_cpus::get()); let (tx, rx) = channel(); for y in 0..height { let tx = tx.clone(); // execute 将每个像素作为单独的作业接收 pool.execute(move || for x in 0..width { let i = julia(c, x, y, width, height, iterations); let pixel = wavelength_to_rgb(380 + i * 400 / iterations); tx.send((x, y, pixel)).expect(\"Could not send data!\"); }); } for _ in 0..(width * height) { let (x, y, pixel) = rx.recv()?; // 使用数据来设置像素的颜色 img.put_pixel(x, y, pixel); } // 输出图片内容到指定文件中 let _ = img.save(\"output.png\")?; Ok(())\n}","breadcrumbs":"并发 » 线程 » 使用线程池来绘制分形","id":"246","title":"使用线程池来绘制分形"},"247":{"body":"","breadcrumbs":"并发 » 使用rayon并行处理数据 » 任务并行处理","id":"247","title":"任务并行处理"},"248":{"body":"rayon 提供了一个 par_iter_mut 方法用于并行化迭代一个数据集合。 use rayon::prelude::*; fn main() { let mut arr = [0, 7, 9, 11]; arr.par_iter_mut().for_each(|p| *p -= 1); println!(\"{:?}\", arr);\n}","breadcrumbs":"并发 » 使用rayon并行处理数据 » 并行修改数组中的元素","id":"248","title":"并行修改数组中的元素"},"249":{"body":"rayon::any 和 rayon::all 类似于 std::any / std::all ,但是是并行版本的。 rayon::any 并行检查迭代器中是否有任何元素满足给定的条件,一旦发现符合条件的元素,就立即返回 rayon::all 并行检查迭代器中的所有元素是否满足给定的条件,一旦发现不满足条件的元素,就立即返回 use rayon::prelude::*; fn main() { let mut vec = vec![2, 4, 6, 8]; assert!(!vec.par_iter().any(|n| (*n % 2) != 0)); assert!(vec.par_iter().all(|n| (*n % 2) == 0)); assert!(!vec.par_iter().any(|n| *n > 8 )); assert!(vec.par_iter().all(|n| *n <= 8 )); vec.push(9); assert!(vec.par_iter().any(|n| (*n % 2) != 0)); assert!(!vec.par_iter().all(|n| (*n % 2) == 0)); assert!(vec.par_iter().any(|n| *n > 8 )); assert!(!vec.par_iter().all(|n| *n <= 8 )); }","breadcrumbs":"并发 » 使用rayon并行处理数据 » 并行测试集合中的元素是否满足给定的条件","id":"249","title":"并行测试集合中的元素是否满足给定的条件"},"25":{"body":"swc 是Typescript/Javascript编译器,它可以用来编译、压缩和打包JS,同时支持使用插件进行扩展,例如做代码变换等。 swc目前正在被一些知名项目所使用,包括Next.js,Parcel和Deno,还有些著名的公司也在使用它,例如Vercel、字节跳动、腾讯等。 它的性能非常非常高,官方号称,在单线程下比Babel快20倍,在4核心下比Babel快70倍! 几个使用案例: nextjs 12 , 通过使用swc获得了更好的扩展性、性能以及wasm的支持,其中性能方面提升了3倍刷新速度、5倍打包速度 Parcel ,通过使用swc改善了10倍的性能","breadcrumbs":"Rust 明星项目 » swc 🌟19.5k","id":"25","title":"swc 🌟19.5k"},"250":{"body":"下面例子使用 par_iter 和 rayon::find_any 来并行搜索一个数组,直到找到任意一个满足条件的元素。 如果有多个元素满足条件,rayon 会返回第一个找到的元素,注意:第一个找到的元素未必是数组中的顺序最靠前的那个。 use rayon::prelude::*; fn main() { let v = vec![6, 2, 1, 9, 3, 8, 11]; // 这里使用了 `&&x` 的形式,大家可以在以下链接阅读更多 https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.find let f1 = v.par_iter().find_any(|&&x| x == 9); let f2 = v.par_iter().find_any(|&&x| x % 2 == 0 && x > 6); let f3 = v.par_iter().find_any(|&&x| x > 8); assert_eq!(f1, Some(&9)); assert_eq!(f2, Some(&8)); assert!(f3 > Some(&8));\n}","breadcrumbs":"并发 » 使用rayon并行处理数据 » 使用给定条件并行搜索","id":"250","title":"使用给定条件并行搜索"},"251":{"body":"下面的例子将对字符串数组进行并行排序。 par_sort_unstable 方法的排序性能往往要比 稳定的排序算法 更高。 use rand::{Rng, thread_rng};\nuse rand::distributions::Alphanumeric;\nuse rayon::prelude::*; fn main() { let mut vec = vec![String::new(); 100_000]; // 并行生成数组中的字符串 vec.par_iter_mut().for_each(|p| { let mut rng = thread_rng(); *p = (0..5).map(|_| rng.sample(&Alphanumeric)).collect() }); // vec.par_sort_unstable();\n}","breadcrumbs":"并发 » 使用rayon并行处理数据 » 对数组进行并行排序","id":"251","title":"对数组进行并行排序"},"252":{"body":"下面例子使用 rayon::filter , rayon::map , 和 rayon::reduce 来超过 30 岁的 Person 的平均年龄。 rayon::filter 返回集合中所有满足给定条件的元素 rayon::map 对集合中的每一个元素执行一个操作,创建并返回新的迭代器,类似于 迭代器适配器 rayon::reduce 则迭代器的元素进行不停的聚合运算,直到获取一个最终结果,这个结果跟例子中 rayon::sum 获取的结果是相同的 use rayon::prelude::*; struct Person { age: u32,\n} fn main() { let v: Vec = vec![ Person { age: 23 }, Person { age: 19 }, Person { age: 42 }, Person { age: 17 }, Person { age: 17 }, Person { age: 31 }, Person { age: 30 }, ]; let num_over_30 = v.par_iter().filter(|&x| x.age > 30).count() as f32; let sum_over_30 = v.par_iter() .map(|x| x.age) .filter(|&x| x > 30) .reduce(|| 0, |x, y| x + y); let alt_sum_30: u32 = v.par_iter() .map(|x| x.age) .filter(|&x| x > 30) .sum(); let avg_over_30 = sum_over_30 as f32 / num_over_30; let alt_avg_over_30 = alt_sum_30 as f32/ num_over_30; assert!((avg_over_30 - alt_avg_over_30).abs() < std::f32::EPSILON); println!(\"The average age of people older than 30 is {}\", avg_over_30);\n}","breadcrumbs":"并发 » 使用rayon并行处理数据 » 并行化 Map-Reuduce","id":"252","title":"并行化 Map-Reuduce"},"253":{"body":"下面例子将为目录中的所有图片并行生成缩略图,然后将结果存到新的目录 thumbnails 中。 glob::glob_with 可以找出当前目录下的所有 .jpg 文件,rayon 通过 DynamicImage::resize 来并行调整图片的大小。 # use error_chain::error_chain; use std::path::Path;\nuse std::fs::create_dir_all; # use error_chain::ChainedError;\nuse glob::{glob_with, MatchOptions};\nuse image::{FilterType, ImageError};\nuse rayon::prelude::*; # error_chain! {\n# foreign_links {\n# Image(ImageError);\n# Io(std::io::Error);\n# Glob(glob::PatternError);\n# }\n#} fn main() -> Result<()> { let options: MatchOptions = Default::default(); // 找到当前目录中的所有 `jpg` 文件 let files: Vec<_> = glob_with(\"*.jpg\", options)? .filter_map(|x| x.ok()) .collect(); if files.len() == 0 { error_chain::bail!(\"No .jpg files found in current directory\"); } let thumb_dir = \"thumbnails\"; create_dir_all(thumb_dir)?; println!(\"Saving {} thumbnails into '{}'...\", files.len(), thumb_dir); let image_failures: Vec<_> = files .par_iter() .map(|path| { make_thumbnail(path, thumb_dir, 300) .map_err(|e| e.chain_err(|| path.display().to_string())) }) .filter_map(|x| x.err()) .collect(); image_failures.iter().for_each(|x| println!(\"{}\", x.display_chain())); println!(\"{} thumbnails saved successfully\", files.len() - image_failures.len()); Ok(())\n} fn make_thumbnail(original: PA, thumb_dir: PB, longest_edge: u32) -> Result<()>\nwhere PA: AsRef, PB: AsRef,\n{ let img = image::open(original.as_ref())?; let file_path = thumb_dir.as_ref().join(original); Ok(img.resize(longest_edge, longest_edge, FilterType::Nearest) .save(file_path)?)\n}","breadcrumbs":"并发 » 使用rayon并行处理数据 » 并行生成缩略图","id":"253","title":"并行生成缩略图"},"254":{"body":"","breadcrumbs":"数据库 » SQLite » SQLite","id":"254","title":"SQLite"},"255":{"body":"使用 rusqlite 可以创建 SQLite 数据库, Connection::open 会尝试打开一个数据库,若不存在,则创建新的数据库。 这里创建的 cats.db 数据库将被后面的例子所使用 use rusqlite::{Connection, Result};\nuse rusqlite::NO_PARAMS; fn main() -> Result<()> { let conn = Connection::open(\"cats.db\")?; conn.execute( \"create table if not exists cat_colors ( id integer primary key, name text not null unique )\", NO_PARAMS, )?; conn.execute( \"create table if not exists cats ( id integer primary key, name text not null, color_id integer not null references cat_colors(id) )\", NO_PARAMS, )?; Ok(())\n}","breadcrumbs":"数据库 » SQLite » 创建 SQLite 数据库","id":"255","title":"创建 SQLite 数据库"},"256":{"body":"use rusqlite::NO_PARAMS;\nuse rusqlite::{Connection, Result};\nuse std::collections::HashMap; #[derive(Debug)]\nstruct Cat { name: String, color: String,\n} fn main() -> Result<()> { // 打开第一个例子所创建的数据库 let conn = Connection::open(\"cats.db\")?; let mut cat_colors = HashMap::new(); cat_colors.insert(String::from(\"Blue\"), vec![\"Tigger\", \"Sammy\"]); cat_colors.insert(String::from(\"Black\"), vec![\"Oreo\", \"Biscuit\"]); for (color, catnames) in &cat_colors { // 插入一条数据行 conn.execute( \"INSERT INTO cat_colors (name) values (?1)\", &[&color.to_string()], )?; // 获取最近插入数据行的 id let last_id: String = conn.last_insert_rowid().to_string(); for cat in catnames { conn.execute( \"INSERT INTO cats (name, color_id) values (?1, ?2)\", &[&cat.to_string(), &last_id], )?; } } let mut stmt = conn.prepare( \"SELECT c.name, cc.name from cats c INNER JOIN cat_colors cc ON cc.id = c.color_id;\", )?; let cats = stmt.query_map(NO_PARAMS, |row| { Ok(Cat { name: row.get(0)?, color: row.get(1)?, }) })?; for cat in cats { println!(\"Found cat {:?}\", cat); } Ok(())\n}","breadcrumbs":"数据库 » SQLite » 插入和查询","id":"256","title":"插入和查询"},"257":{"body":"使用 Connection::transaction 可以开始新的事务,若没有对事务进行显式地提交 Transaction::commit ,则会进行回滚。 下面的例子中,rolled_back_tx 插入了重复的颜色名称,会发生回滚。 use rusqlite::{Connection, Result, NO_PARAMS}; fn main() -> Result<()> { // 打开第一个例子所创建的数据库 let mut conn = Connection::open(\"cats.db\")?; successful_tx(&mut conn)?; let res = rolled_back_tx(&mut conn); assert!(res.is_err()); Ok(())\n} fn successful_tx(conn: &mut Connection) -> Result<()> { let tx = conn.transaction()?; tx.execute(\"delete from cat_colors\", NO_PARAMS)?; tx.execute(\"insert into cat_colors (name) values (?1)\", &[&\"lavender\"])?; tx.execute(\"insert into cat_colors (name) values (?1)\", &[&\"blue\"])?; tx.commit()\n} fn rolled_back_tx(conn: &mut Connection) -> Result<()> { let tx = conn.transaction()?; tx.execute(\"delete from cat_colors\", NO_PARAMS)?; tx.execute(\"insert into cat_colors (name) values (?1)\", &[&\"lavender\"])?; tx.execute(\"insert into cat_colors (name) values (?1)\", &[&\"blue\"])?; tx.execute(\"insert into cat_colors (name) values (?1)\", &[&\"lavender\"])?; tx.commit()\n}","breadcrumbs":"数据库 » SQLite » 使用事务","id":"257","title":"使用事务"},"258":{"body":"","breadcrumbs":"数据库 » Postgres » Postgres","id":"258","title":"Postgres"},"259":{"body":"我们通过 postgres 来操作数据库。下面的例子有一个前提:数据库 library 已经存在,其中用户名和密码都是 postgres。 use postgres::{Client, NoTls, Error}; fn main() -> Result<(), Error> { // 连接到数据库 library let mut client = Client::connect(\"postgresql://postgres:postgres@localhost/library\", NoTls)?; client.batch_execute(\" CREATE TABLE IF NOT EXISTS author ( id SERIAL PRIMARY KEY, name VARCHAR NOT NULL, country VARCHAR NOT NULL ) \")?; client.batch_execute(\" CREATE TABLE IF NOT EXISTS book ( id SERIAL PRIMARY KEY, title VARCHAR NOT NULL, author_id INTEGER NOT NULL REFERENCES author ) \")?; Ok(()) }","breadcrumbs":"数据库 » Postgres » 在数据库中创建表格","id":"259","title":"在数据库中创建表格"},"26":{"body":"tauri 可以用来更小、更快、更安全的桌面应用,它想要替代的是electro.js。 下面是援引自 官网 的性能对比图:","breadcrumbs":"Rust 明星项目 » tauri","id":"26","title":"tauri"},"260":{"body":"use postgres::{Client, NoTls, Error};\nuse std::collections::HashMap; struct Author { _id: i32, name: String, country: String\n} fn main() -> Result<(), Error> { let mut client = Client::connect(\"postgresql://postgres:postgres@localhost/library\", NoTls)?; let mut authors = HashMap::new(); authors.insert(String::from(\"Chinua Achebe\"), \"Nigeria\"); authors.insert(String::from(\"Rabindranath Tagore\"), \"India\"); authors.insert(String::from(\"Anita Nair\"), \"India\"); for (key, value) in &authors { let author = Author { _id: 0, name: key.to_string(), country: value.to_string() }; // 插入数据 client.execute( \"INSERT INTO author (name, country) VALUES ($1, $2)\", &[&author.name, &author.country], )?; } // 查询数据 for row in client.query(\"SELECT id, name, country FROM author\", &[])? { let author = Author { _id: row.get(0), name: row.get(1), country: row.get(2), }; println!(\"Author {} is from {}\", author.name, author.country); } Ok(()) }","breadcrumbs":"数据库 » Postgres » 插入和查询","id":"260","title":"插入和查询"},"261":{"body":"下面代码将使用降序的方式列出 Museum of Modern Art 数据库中的前 7999 名艺术家的国籍分布. use postgres::{Client, Error, NoTls}; struct Nation { nationality: String, count: i64,\n} fn main() -> Result<(), Error> { let mut client = Client::connect( \"postgresql://postgres:postgres@127.0.0.1/moma\", NoTls, )?; for row in client.query (\"SELECT nationality, COUNT(nationality) AS count FROM artists GROUP BY nationality ORDER BY count DESC\", &[])? { let (nationality, count) : (Option, Option) = (row.get (0), row.get (1)); if nationality.is_some () && count.is_some () { let nation = Nation{ nationality: nationality.unwrap(), count: count.unwrap(), }; println!(\"{} {}\", nation.nationality, nation.count); } } Ok(())\n}","breadcrumbs":"数据库 » Postgres » 聚合数据","id":"261","title":"聚合数据"},"262":{"body":"","breadcrumbs":"日期和时间 » 时间计算和转换 » 时间计算和转换","id":"262","title":"时间计算和转换"},"263":{"body":"测量从 time::Instant::now 开始所经过的时间 time::Instant::elapsed . use std::time::{Duration, Instant}; fn main() { let start = Instant::now(); expensive_function(); let duration = start.elapsed(); println!(\"Time elapsed in expensive_function() is: {:?}\", duration);\n}","breadcrumbs":"日期和时间 » 时间计算和转换 » 测量某段代码的耗时","id":"263","title":"测量某段代码的耗时"},"264":{"body":"使用 DateTime::checked_add_signed 计算和显示从现在开始两周后的日期和时间,然后再计算一天前的日期 DateTime::checked_sub_signed 。 DateTime::format 所支持的转义序列可以在 chrono::format::strftime 找到. use chrono::{DateTime, Duration, Utc}; fn day_earlier(date_time: DateTime) -> Option> { date_time.checked_sub_signed(Duration::days(1))\n} fn main() { let now = Utc::now(); println!(\"{}\", now); let almost_three_weeks_from_now = now.checked_add_signed(Duration::weeks(2)) .and_then(|in_2weeks| in_2weeks.checked_add_signed(Duration::weeks(1))) .and_then(day_earlier); match almost_three_weeks_from_now { Some(x) => println!(\"{}\", x), None => eprintln!(\"Almost three weeks from now overflows!\"), } match now.checked_add_signed(Duration::max_value()) { Some(x) => println!(\"{}\", x), None => eprintln!(\"We can't use chrono to tell the time for the Solar System to complete more than one full orbit around the galactic center.\"), }\n}","breadcrumbs":"日期和时间 » 时间计算和转换 » 对日期和时间进行计算","id":"264","title":"对日期和时间进行计算"},"265":{"body":"使用 offset::Local::now 获取本地时间并进行显示,接着,使用 DateTime::from_utc 将它转换成 UTC 标准时间。最后,再使用 offset::FixedOffset 将 UTC 时间转换成 UTC+8 和 UTC-2 的时间。 use chrono::{DateTime, FixedOffset, Local, Utc}; fn main() { let local_time = Local::now(); let utc_time = DateTime::::from_utc(local_time.naive_utc(), Utc); let china_timezone = FixedOffset::east(8 * 3600); let rio_timezone = FixedOffset::west(2 * 3600); println!(\"Local time now is {}\", local_time); println!(\"UTC time now is {}\", utc_time); println!( \"Time in Hong Kong now is {}\", utc_time.with_timezone(&china_timezone) ); println!(\"Time in Rio de Janeiro now is {}\", utc_time.with_timezone(&rio_timezone));\n}","breadcrumbs":"日期和时间 » 时间计算和转换 » 将本地时间转换成其它时区","id":"265","title":"将本地时间转换成其它时区"},"266":{"body":"","breadcrumbs":"日期和时间 » 解析和显示 » 解析和显示","id":"266","title":"解析和显示"},"267":{"body":"通过 DateTime 获取当前的 UTC 时间: Timelike , 时/分/秒 Datelike , 年/月/日 use chrono::{Datelike, Timelike, Utc}; fn main() { let now = Utc::now(); let (is_pm, hour) = now.hour12(); println!( \"The current UTC time is {:02}:{:02}:{:02} {}\", hour, now.minute(), now.second(), if is_pm { \"PM\" } else { \"AM\" } ); println!( \"And there have been {} seconds since midnight\", now.num_seconds_from_midnight() ); let (is_common_era, year) = now.year_ce(); println!( \"The current UTC date is {}-{:02}-{:02} {:?} ({})\", year, now.month(), now.day(), now.weekday(), if is_common_era { \"CE\" } else { \"BCE\" } ); println!( \"And the Common Era began {} days ago\", now.num_days_from_ce() );\n}","breadcrumbs":"日期和时间 » 解析和显示 » 检查日期和时间","id":"267","title":"检查日期和时间"},"268":{"body":"use chrono::{NaiveDate, NaiveDateTime}; fn main() { // 生成一个具体的日期时间 let date_time: NaiveDateTime = NaiveDate::from_ymd(2017, 11, 12).and_hms(17, 33, 44); println!( \"Number of seconds between 1970-01-01 00:00:00 and {} is {}.\", // 打印日期和日期对应的时间戳 date_time, date_time.timestamp()); // 计算从 1970 1月1日 0:00:00 UTC 开始,10亿秒后是什么日期时间 let date_time_after_a_billion_seconds = NaiveDateTime::from_timestamp(1_000_000_000, 0); println!( \"Date after a billion seconds since 1970-01-01 00:00:00 was {}.\", date_time_after_a_billion_seconds);\n}","breadcrumbs":"日期和时间 » 解析和显示 » 日期和时间戳的相互转换","id":"268","title":"日期和时间戳的相互转换"},"269":{"body":"通过 Utc::now 可以获取当前的 UTC 时间。 use chrono::{DateTime, Utc}; fn main() { let now: DateTime = Utc::now(); println!(\"UTC now is: {}\", now); // 使用 RFC 2822 格式显示当前时间 println!(\"UTC now in RFC 2822 is: {}\", now.to_rfc2822()); // 使用 RFC 3339 格式显示当前时间 println!(\"UTC now in RFC 3339 is: {}\", now.to_rfc3339()); // 使用自定义格式显示当前时间 println!(\"UTC now in a custom format is: {}\", now.format(\"%a %b %e %T %Y\"));\n}","breadcrumbs":"日期和时间 » 解析和显示 » 显示格式化的日期和时间","id":"269","title":"显示格式化的日期和时间"},"27":{"body":"yew 是一个正在活跃开发的Rust/Wasm框架,用于构建Web应用。","breadcrumbs":"Rust 明星项目 » yew","id":"27","title":"yew"},"270":{"body":"我们可以将多种格式的日期时间字符串转换成 DateTime 结构体。 DateTime::parse_from_str 使用的转义序列可以在 chrono::format::strftime 找到. 只有当能唯一的标识出日期和时间时,才能创建 DateTime。如果要在没有时区的情况下解析日期或时间,你需要使用 NativeDate 等函数。 use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime};\nuse chrono::format::ParseError; fn main() -> Result<(), ParseError> { let rfc2822 = DateTime::parse_from_rfc2822(\"Tue, 1 Jul 2003 10:52:37 +0200\")?; println!(\"{}\", rfc2822); let rfc3339 = DateTime::parse_from_rfc3339(\"1996-12-19T16:39:57-08:00\")?; println!(\"{}\", rfc3339); let custom = DateTime::parse_from_str(\"5.8.1994 8:00 am +0000\", \"%d.%m.%Y %H:%M %P %z\")?; println!(\"{}\", custom); let time_only = NaiveTime::parse_from_str(\"23:56:04\", \"%H:%M:%S\")?; println!(\"{}\", time_only); let date_only = NaiveDate::parse_from_str(\"2015-09-05\", \"%Y-%m-%d\")?; println!(\"{}\", date_only); let no_timezone = NaiveDateTime::parse_from_str(\"2015-09-05 23:56:04\", \"%Y-%m-%d %H:%M:%S\")?; println!(\"{}\", no_timezone); Ok(())\n}","breadcrumbs":"日期和时间 » 解析和显示 » 将字符串解析为 DateTime 结构体","id":"270","title":"将字符串解析为 DateTime 结构体"},"271":{"body":"","breadcrumbs":"开发者工具 » 日志 » 日志","id":"271","title":"日志"},"272":{"body":"log 提供了日志相关的实用工具。","breadcrumbs":"开发者工具 » 日志 » log 包","id":"272","title":"log 包"},"273":{"body":"env_logger 通过环境变量来配置日志。 log::debug! 使用起来跟 std::fmt 中的格式化字符串很像。 fn execute_query(query: &str) { log::debug!(\"Executing query: {}\", query);\n} fn main() { env_logger::init(); execute_query(\"DROP TABLE students\");\n} 如果大家运行代码,会发现没有任何日志输出,原因是默认的日志级别是 error,因此我们需要通过 RUST_LOG 环境变量来设置下新的日志级别: $ RUST_LOG=debug cargo run 然后你将成功看到以下输出: DEBUG:main: Executing query: DROP TABLE students","breadcrumbs":"开发者工具 » 日志 » 在控制台打印 debug 信息","id":"273","title":"在控制台打印 debug 信息"},"274":{"body":"下面我们通过 log::error! 将错误日志输出到标准错误 stderr。 fn execute_query(_query: &str) -> Result<(), &'static str> { Err(\"I'm afraid I can't do that\")\n} fn main() { env_logger::init(); let response = execute_query(\"DROP TABLE students\"); if let Err(err) = response { log::error!(\"Failed to execute query: {}\", err); }\n}","breadcrumbs":"开发者工具 » 日志 » 将错误日志输出到控制台","id":"274","title":"将错误日志输出到控制台"},"275":{"body":"默认的错误会输出到标准错误输出 stderr,下面我们通过自定的配置来让错误输出到标准输出 stdout。 use env_logger::{Builder, Target}; fn main() { Builder::new() .target(Target::Stdout) .init(); log::error!(\"This error has been printed to Stdout\");\n}","breadcrumbs":"开发者工具 » 日志 » 将错误输出到标准输出 stdout","id":"275","title":"将错误输出到标准输出 stdout"},"276":{"body":"下面的代码将实现一个自定义 logger ConsoleLogger,输出到标准输出 stdout。为了使用日志宏,ConsoleLogger 需要实现 log::Log 特征,然后使用 log::set_logger 来安装使用。 use log::{Record, Level, Metadata, LevelFilter, SetLoggerError}; static CONSOLE_LOGGER: ConsoleLogger = ConsoleLogger; struct ConsoleLogger; impl log::Log for ConsoleLogger { fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= Level::Info } fn log(&self, record: &Record) { if self.enabled(record.metadata()) { println!(\"Rust says: {} - {}\", record.level(), record.args()); } } fn flush(&self) {}\n} fn main() -> Result<(), SetLoggerError> { log::set_logger(&CONSOLE_LOGGER)?; log::set_max_level(LevelFilter::Info); log::info!(\"hello log\"); log::warn!(\"warning\"); log::error!(\"oops\"); Ok(())\n}","breadcrumbs":"开发者工具 » 日志 » 使用自定义 logger","id":"276","title":"使用自定义 logger"},"277":{"body":"下面的代码将使用 syslog 包将日志输出到 Unix Syslog . #[cfg(target_os = \"linux\")]\n#[cfg(target_os = \"linux\")]\nuse syslog::{Facility, Error}; #[cfg(target_os = \"linux\")]\nfn main() -> Result<(), Error> { // 初始化 logger syslog::init(Facility::LOG_USER, log::LevelFilter::Debug, // 可选的应用名称 Some(\"My app name\"))?; log::debug!(\"this is a debug {}\", \"message\"); log::error!(\"this is an error!\"); Ok(())\n} #[cfg(not(target_os = \"linux\"))]\nfn main() { println!(\"So far, only Linux systems are supported.\");\n}","breadcrumbs":"开发者工具 » 日志 » 输出到 Unix syslog","id":"277","title":"输出到 Unix syslog"},"278":{"body":"@todo","breadcrumbs":"开发者工具 » 日志 » tracing","id":"278","title":"tracing"},"279":{"body":"","breadcrumbs":"开发者工具 » 配置日志 » 配置日志","id":"279","title":"配置日志"},"28":{"body":"firecracker 是一个安全、高性能的无服务计算虚拟机(FaaS),支持多租户、资源隔离等高级特性,由Amazon公司开发,为AWS部分云计算服务提供了强力有的支持。BTW,亚马逊Amazon公司对于Rust语言的喜爱是众所周知的,几乎已经成了Rust的形象大使之一了:)","breadcrumbs":"Rust 明星项目 » firecracker","id":"28","title":"firecracker"},"280":{"body":"下面代码创建了模块 foo 和嵌套模块 foo::bar,并通过 RUST_LOG 环境变量对各自的日志级别进行了控制。 mod foo { mod bar { pub fn run() { log::warn!(\"[bar] warn\"); log::info!(\"[bar] info\"); log::debug!(\"[bar] debug\"); } } pub fn run() { log::warn!(\"[foo] warn\"); log::info!(\"[foo] info\"); log::debug!(\"[foo] debug\"); bar::run(); }\n} fn main() { env_logger::init(); log::warn!(\"[root] warn\"); log::info!(\"[root] info\"); log::debug!(\"[root] debug\"); foo::run();\n} 要让环境变量生效,首先需要通过 env_logger::init() 开启相关的支持。然后通过以下命令来运行程序: RUST_LOG=\"warn,test::foo=info,test::foo::bar=debug\" ./test 此时的默认日志级别被设置为 warn,但我们还将 foo 模块级别设置为 info, foo::bar 模块日志级别设置为 debug。 WARN:test: [root] warn\nWARN:test::foo: [foo] warn\nINFO:test::foo: [foo] info\nWARN:test::foo::bar: [bar] warn\nINFO:test::foo::bar: [bar] info\nDEBUG:test::foo::bar: [bar] debug","breadcrumbs":"开发者工具 » 配置日志 » 为每个模块开启独立的日志级别","id":"280","title":"为每个模块开启独立的日志级别"},"281":{"body":"Builder 将对日志进行配置,以下代码使用 MY_APP_LOG 来替代 RUST_LOG 环境变量: use std::env;\nuse env_logger::Builder; fn main() { Builder::new() .parse(&env::var(\"MY_APP_LOG\").unwrap_or_default()) .init(); log::info!(\"informational message\"); log::warn!(\"warning message\"); log::error!(\"this is an error {}\", \"message\");\n}","breadcrumbs":"开发者工具 » 配置日志 » 使用自定义环境变量来设置日志","id":"281","title":"使用自定义环境变量来设置日志"},"282":{"body":"use std::io::Write;\nuse chrono::Local;\nuse env_logger::Builder;\nuse log::LevelFilter; fn main() { Builder::new() .format(|buf, record| { writeln!(buf, \"{} [{}] - {}\", Local::now().format(\"%Y-%m-%dT%H:%M:%S\"), record.level(), record.args() ) }) .filter(None, LevelFilter::Info) .init(); log::warn!(\"warn\"); log::info!(\"info\"); log::debug!(\"debug\");\n} 以下是 stderr 的输出: 2022-03-22T21:57:06 [WARN] - warn\n2022-03-22T21:57:06 [INFO] - info","breadcrumbs":"开发者工具 » 配置日志 » 在日志中包含时间戳","id":"282","title":"在日志中包含时间戳"},"283":{"body":"log4rs 可以帮我们将日志输出指定的位置,它可以使用外部 YAML 文件或 builder 的方式进行配置。 # use error_chain::error_chain; use log::LevelFilter;\nuse log4rs::append::file::FileAppender;\nuse log4rs::encode::pattern::PatternEncoder;\nuse log4rs::config::{Appender, Config, Root}; #error_chain! {\n# foreign_links {\n# Io(std::io::Error);\n# LogConfig(log4rs::config::Errors);\n# SetLogger(log::SetLoggerError);\n# }\n#} fn main() -> Result<()> { // 创建日志配置,并指定输出的位置 let logfile = FileAppender::builder() // 编码模式的详情参见: https://docs.rs/log4rs/1.0.0/log4rs/encode/pattern/index.html .encoder(Box::new(PatternEncoder::new(\"{l} - {m}\\n\"))) .build(\"log/output.log\")?; let config = Config::builder() .appender(Appender::builder().build(\"logfile\", Box::new(logfile))) .build(Root::builder() .appender(\"logfile\") .build(LevelFilter::Info))?; log4rs::init_config(config)?; log::info!(\"Hello, world!\"); Ok(())\n}","breadcrumbs":"开发者工具 » 配置日志 » 将日志输出到指定文件","id":"283","title":"将日志输出到指定文件"},"284":{"body":"","breadcrumbs":"开发者工具 » 版本号 » 版本号","id":"284","title":"版本号"},"285":{"body":"下面例子使用 Version::parse 将一个字符串转换成 semver::Version 版本号,然后将它的 patch, minor, major 版本号都增加 1。 注意,为了符合 语义化版本的说明 ,增加 minor 版本时,patch 版本会被重设为 0,当增加 major 版本时,minor 和 patch 都将被重设为 0。 use semver::{Version, SemVerError}; fn main() -> Result<(), SemVerError> { let mut parsed_version = Version::parse(\"0.2.6\")?; assert_eq!( parsed_version, Version { major: 0, minor: 2, patch: 6, pre: vec![], build: vec![], } ); parsed_version.increment_patch(); assert_eq!(parsed_version.to_string(), \"0.2.7\"); println!(\"New patch release: v{}\", parsed_version); parsed_version.increment_minor(); assert_eq!(parsed_version.to_string(), \"0.3.0\"); println!(\"New minor release: v{}\", parsed_version); parsed_version.increment_major(); assert_eq!(parsed_version.to_string(), \"1.0.0\"); println!(\"New major release: v{}\", parsed_version); Ok(())\n}","breadcrumbs":"开发者工具 » 版本号 » 解析并增加版本号","id":"285","title":"解析并增加版本号"},"286":{"body":"这里的版本号字符串还将包含 SemVer 中定义的预发布和构建元信息。 值得注意的是,为了符合 SemVer 的规则,构建元信息虽然会被解析,但是在做版本号比较时,该信息会被忽略。换而言之,即使两个版本号的构建字符串不同,它们的版本号依然可能相同。 use semver::{Identifier, Version, SemVerError}; fn main() -> Result<(), SemVerError> { let version_str = \"1.0.49-125+g72ee7853\"; let parsed_version = Version::parse(version_str)?; assert_eq!( parsed_version, Version { major: 1, minor: 0, patch: 49, pre: vec![Identifier::Numeric(125)], build: vec![], } ); assert_eq!( parsed_version.build, vec![Identifier::AlphaNumeric(String::from(\"g72ee7853\"))] ); let serialized_version = parsed_version.to_string(); assert_eq!(&serialized_version, version_str); Ok(())\n}","breadcrumbs":"开发者工具 » 版本号 » 解析一个复杂的版本号字符串","id":"286","title":"解析一个复杂的版本号字符串"},"287":{"body":"下面例子给出两个版本号,然后通过 is_prerelease 判断哪个是预发布的版本号。 use semver::{Version, SemVerError}; fn main() -> Result<(), SemVerError> { let version_1 = Version::parse(\"1.0.0-alpha\")?; let version_2 = Version::parse(\"1.0.0\")?; assert!(version_1.is_prerelease()); assert!(!version_2.is_prerelease()); Ok(())\n}","breadcrumbs":"开发者工具 » 版本号 » 检查给定的版本号是否是预发布","id":"287","title":"检查给定的版本号是否是预发布"},"288":{"body":"下面例子给出了一个版本号列表,我们需要找到其中最新的版本。 #use error_chain::error_chain; use semver::{Version, VersionReq}; #error_chain! {\n# foreign_links {\n# SemVer(semver::SemVerError);\n# SemVerReq(semver::ReqParseError);\n# }\n3} fn find_max_matching_version<'a, I>(version_req_str: &str, iterable: I) -> Result>\nwhere I: IntoIterator,\n{ let vreq = VersionReq::parse(version_req_str)?; Ok( iterable .into_iter() .filter_map(|s| Version::parse(s).ok()) .filter(|s| vreq.matches(s)) .max(), )\n} fn main() -> Result<()> { assert_eq!( find_max_matching_version(\"<= 1.0.0\", vec![\"0.9.0\", \"1.0.0\", \"1.0.1\"])?, Some(Version::parse(\"1.0.0\")?) ); assert_eq!( find_max_matching_version( \">1.2.3-alpha.3\", vec![ \"1.2.3-alpha.3\", \"1.2.3-alpha.4\", \"1.2.3-alpha.10\", \"1.2.3-beta.4\", \"3.4.5-alpha.9\", ] )?, Some(Version::parse(\"1.2.3-beta.4\")?) ); Ok(())\n}","breadcrumbs":"开发者工具 » 版本号 » 找出给定范围内的最新版本","id":"288","title":"找出给定范围内的最新版本"},"289":{"body":"下面将通过 Command 来执行系统命令 git --version,并对该系统命令返回的 git 版本号进行解析。 #use error_chain::error_chain; use std::process::Command;\nuse semver::{Version, VersionReq}; #error_chain! {\n# foreign_links {\n# Io(std::io::Error);\n# Utf8(std::string::FromUtf8Error);\n# SemVer(semver::SemVerError);\n# SemVerReq(semver::ReqParseError);\n# }\n#} fn main() -> Result<()> { let version_constraint = \"> 1.12.0\"; let version_test = VersionReq::parse(version_constraint)?; let output = Command::new(\"git\").arg(\"--version\").output()?; if !output.status.success() { error_chain::bail!(\"Command executed with failing error code\"); } let stdout = String::from_utf8(output.stdout)?; let version = stdout.split(\" \").last().ok_or_else(|| { \"Invalid command output\" })?; let parsed_version = Version::parse(version)?; if !version_test.matches(&parsed_version) { error_chain::bail!(\"Command version lower than minimum supported version (found {}, need {})\", parsed_version, version_constraint); } Ok(())\n}","breadcrumbs":"开发者工具 » 版本号 » 检查外部命令的版本号兼容性","id":"289","title":"检查外部命令的版本号兼容性"},"29":{"body":"nushell 是一个全新的shell,使用Rust实现。它的目标是创建一个现代化的shell:虽然依然基于Unix的哲学,但是更适合现在的时代。例如,你可以使用SQL语法来选择你想要的内容!","breadcrumbs":"Rust 明星项目 » nushell","id":"29","title":"nushell"},"290":{"body":"本章节的内容是关于构建工具的,如果大家没有听说过 build.rs 文件,强烈建议先看看 这里 了解下何为构建工具。","breadcrumbs":"开发者工具 » 构建时工具 » 构建时工具","id":"290","title":"构建时工具"},"291":{"body":"cc 包能帮助我们更好地跟 C/C++/汇编进行交互:它提供了简单的 API 可以将外部的库编译成静态库( .a ),然后通过 rustc 进行静态链接。 下面的例子中,我们将在 Rust 代码中使用 C 的代码: src/hello.c 。在开始编译 Rust 的项目代码前,build.rs 构建脚本将先被执行。通过 cc 包,一个静态的库可以被生成( libhello.a ),然后该库将被 Rust的代码所使用:通过 extern 声明外部函数签名的方式来使用。 由于例子中的 C 代码很简单,因此只需要将一个文件传递给 cc::Build 。如果大家需要更复杂的构建,cc::Build 还提供了通过 include 来包含路径的方式,以及额外的编译标志( flags )。 Cargo.toml [package]\n...\nbuild = \"build.rs\" [build-dependencies]\ncc = \"1\" [dependencies]\nerror-chain = \"0.11\" build.rs fn main() { cc::Build::new() .file(\"src/hello.c\") .compile(\"hello\"); // outputs `libhello.a`\n} src/hello.c #include void hello() { printf(\"Hello from C!\\n\");\n} void greet(const char* name) { printf(\"Hello, %s!\\n\", name);\n} src/main.rs use error_chain::error_chain;\nuse std::ffi::CString;\nuse std::os::raw::c_char; error_chain! { foreign_links { NulError(::std::ffi::NulError); Io(::std::io::Error); }\n}\nfn prompt(s: &str) -> Result { use std::io::Write; print!(\"{}\", s); std::io::stdout().flush()?; let mut input = String::new(); std::io::stdin().read_line(&mut input)?; Ok(input.trim().to_string())\n} extern { fn hello(); fn greet(name: *const c_char);\n} fn main() -> Result<()> { unsafe { hello() } let name = prompt(\"What's your name? \")?; let c_name = CString::new(name)?; unsafe { greet(c_name.as_ptr()) } Ok(())\n}","breadcrumbs":"开发者工具 » 构建时工具 » 编译并静态链接一个 C 库","id":"291","title":"编译并静态链接一个 C 库"},"292":{"body":"链接到 C++ 库跟之前的方式非常相似。主要的区别在于链接到 C++ 库时,你需要通过构建方法 cpp(true) 来指定一个 C++ 编译器,然后在 C++ 的代码顶部添加 extern \"C\" 来阻止 C++ 编译器对库名进行名称重整( name mangling )。 Cargo.toml [package]\n...\nbuild = \"build.rs\" [build-dependencies]\ncc = \"1\" build.rs fn main() { cc::Build::new() .cpp(true) .file(\"src/foo.cpp\") .compile(\"foo\"); } src/foo.cpp extern \"C\" { int multiply(int x, int y);\n} int multiply(int x, int y) { return x*y;\n} src/main.rs extern { fn multiply(x : i32, y : i32) -> i32;\n} fn main(){ unsafe { println!(\"{}\", multiply(5,7)); } }","breadcrumbs":"开发者工具 » 构建时工具 » 编译并静态链接一个 C++ 库","id":"292","title":"编译并静态链接一个 C++ 库"},"293":{"body":"cc::Build::define 可以让我们使用自定义的 define 来构建 C 库。 以下示例在构建脚本 build.rs 中动态定义了一个 define,然后在运行时打印出 Welcome to foo - version 1.0.2 。Cargo 会设置一些 环境变量 ,它们对于自定义的 define 会有所帮助。 Cargo.toml [package]\n...\nversion = \"1.0.2\"\nbuild = \"build.rs\" [build-dependencies]\ncc = \"1\" build.rs fn main() { cc::Build::new() .define(\"APP_NAME\", \"\\\"foo\\\"\") .define(\"VERSION\", format!(\"\\\"{}\\\"\", env!(\"CARGO_PKG_VERSION\")).as_str()) .define(\"WELCOME\", None) .file(\"src/foo.c\") .compile(\"foo\");\n} src/foo.c #include void print_app_info() {\n#ifdef WELCOME printf(\"Welcome to \");\n#endif printf(\"%s - version %s\\n\", APP_NAME, VERSION);\n} src/main.rs extern { fn print_app_info();\n} fn main(){ unsafe { print_app_info(); } }","breadcrumbs":"开发者工具 » 构建时工具 » 为 C 库创建自定义的 define","id":"293","title":"为 C 库创建自定义的 define"},"294":{"body":"","breadcrumbs":"编解码 » 字符编码 » 字符编码","id":"294","title":"字符编码"},"295":{"body":"百分号编码 又称 URL 编码。 percent-encoding 包提供了两个函数:utf8_percent_encode 函数用于编码、percent_decode 用于解码。 use percent_encoding::{utf8_percent_encode, percent_decode, AsciiSet, CONTROLS};\nuse std::str::Utf8Error; /// https://url.spec.whatwg.org/#fragment-percent-encode-set\nconst FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'\"').add(b'<').add(b'>').add(b'`'); fn main() -> Result<(), Utf8Error> { let input = \"confident, productive systems programming\"; let iter = utf8_percent_encode(input, FRAGMENT); // 将元素类型为 &str 的迭代器收集为 String 类型 let encoded: String = iter.collect(); assert_eq!(encoded, \"confident,%20productive%20systems%20programming\"); let iter = percent_decode(encoded.as_bytes()); let decoded = iter.decode_utf8()?; assert_eq!(decoded, \"confident, productive systems programming\"); Ok(())\n} 该编码集定义了哪些字符( 特别是非 ASCII 和控制字符 )需要被百分比编码。具体的选择取决于上下文,例如 url 会对 URL 路径中的 ? 进行编码,但是在路径后的查询字符串中,并不会进行编码。","breadcrumbs":"编解码 » 字符编码 » 百分号编码( Percent encoding )","id":"295","title":"百分号编码( Percent encoding )"},"296":{"body":"使用 form_urlencoded::byte_serialize 函数将一个字符串编码成 application/x-www-form-urlencoded 格式,然后再使用 form_urlencoded::parse 对其进行解码。 use url::form_urlencoded::{byte_serialize, parse}; fn main() { let urlencoded: String = byte_serialize(\"What is ❤?\".as_bytes()).collect(); assert_eq!(urlencoded, \"What+is+%E2%9D%A4%3F\"); println!(\"urlencoded:'{}'\", urlencoded); let decoded: String = parse(urlencoded.as_bytes()) .map(|(key, val)| [key, val].concat()) .collect(); assert_eq!(decoded, \"What is ❤?\"); println!(\"decoded:'{}'\", decoded);\n}","breadcrumbs":"编解码 » 字符编码 » 将字符串编码为 application/x-www-form-urlencoded","id":"296","title":"将字符串编码为 application/x-www-form-urlencoded"},"297":{"body":"data_encoding 可以将一个字符串编码成十六进制字符串,反之亦然。 下面的例子将 &[u8] 转换成十六进制等效形式,然后与期待的值进行比较。 use data_encoding::{HEXUPPER, DecodeError}; fn main() -> Result<(), DecodeError> { let original = b\"The quick brown fox jumps over the lazy dog.\"; let expected = \"54686520717569636B2062726F776E20666F78206A756D7073206F76\\ 657220746865206C617A7920646F672E\"; let encoded = HEXUPPER.encode(original); assert_eq!(encoded, expected); let decoded = HEXUPPER.decode(&encoded.into_bytes())?; assert_eq!(&decoded[..], &original[..]); Ok(())\n}","breadcrumbs":"编解码 » 字符编码 » 十六进制编解码","id":"297","title":"十六进制编解码"},"298":{"body":"base64 可以把一个字节切片编码成 base64 String。 use error_chain::error_chain; use std::str;\nuse base64::{encode, decode}; error_chain! { foreign_links { Base64(base64::DecodeError); Utf8Error(str::Utf8Error); }\n} fn main() -> Result<()> { // 将 `&str` 转换成 `&[u8; N]` let hello = b\"hello rustaceans\"; let encoded = encode(hello); let decoded = decode(&encoded)?; println!(\"origin: {}\", str::from_utf8(hello)?); println!(\"base64 encoded: {}\", encoded); println!(\"back to origin: {}\", str::from_utf8(&decoded)?); Ok(())\n}","breadcrumbs":"编解码 » 字符编码 » Base64 编解码","id":"298","title":"Base64 编解码"},"299":{"body":"","breadcrumbs":"编解码 » CSV » CSV","id":"299","title":"CSV"},"3":{"body":"","breadcrumbs":"日常开发常用库 » 日常开发常用库","id":"3","title":"日常开发常用库"},"30":{"body":"tokio 的名声可以说是如雷贯耳,如果学过Rust但是没有听说过它,那我觉得可能要回炉重造下:) tokio是一个异步IO的运行时,提供了I/O、网络、调度、定时器等等异步编程所必须的功能和工具,性能和功能都异常强大。","breadcrumbs":"Rust 明星项目 » tokio","id":"30","title":"tokio"},"300":{"body":"我们可以将标准的 CSV 记录值读取到 csv::StringRecord 中,但是该数据结构期待合法的 UTF8 数据行,你还可以使用 csv::ByteRecord 来读取非 UTF8 数据。 use csv::Error; fn main() -> Result<(), Error> { let csv = \"year,make,model,description 1948,Porsche,356,Luxury sports car 1967,Ford,Mustang fastback 1967,American car\"; let mut reader = csv::Reader::from_reader(csv.as_bytes()); for record in reader.records() { let record = record?; println!( \"In {}, {} built the {} model. It is a {}.\", &record[0], &record[1], &record[2], &record[3] ); } Ok(())\n} 还可以使用 serde 将数据反序列化成一个强类型的结构体。 use serde::Deserialize;\n#[derive(Deserialize)]\nstruct Record { year: u16, make: String, model: String, description: String,\n} fn main() -> Result<(), csv::Error> { let csv = \"year,make,model,description\n1948,Porsche,356,Luxury sports car\n1967,Ford,Mustang fastback 1967,American car\"; let mut reader = csv::Reader::from_reader(csv.as_bytes()); for record in reader.deserialize() { let record: Record = record?; println!( \"In {}, {} built the {} model. It is a {}.\", record.year, record.make, record.model, record.description ); } Ok(())\n}","breadcrumbs":"编解码 » CSV » 读取 CSV 记录","id":"300","title":"读取 CSV 记录"},"301":{"body":"下面的例子将读取使用了 tab 作为分隔符的 CSV 记录。 use csv::Error;\nuse serde::Deserialize;\n#[derive(Debug, Deserialize)]\nstruct Record { name: String, place: String, #[serde(deserialize_with = \"csv::invalid_option\")] id: Option,\n} use csv::ReaderBuilder; fn main() -> Result<(), Error> { let data = \"name\\tplace\\tid Mark\\tMelbourne\\t46 Ashley\\tZurich\\t92\"; let mut reader = ReaderBuilder::new().delimiter(b'\\t').from_reader(data.as_bytes()); for result in reader.deserialize::() { println!(\"{:?}\", result?); } Ok(())\n}","breadcrumbs":"编解码 » CSV » 读取使用了不同分隔符的 CSV 记录","id":"301","title":"读取使用了不同分隔符的 CSV 记录"},"302":{"body":"use error_chain::error_chain; use std::io; error_chain!{ foreign_links { Io(std::io::Error); CsvError(csv::Error); }\n} fn main() -> Result<()> { let query = \"CA\"; let data = \"\\\nCity,State,Population,Latitude,Longitude\nKenai,AK,7610,60.5544444,-151.2583333\nOakman,AL,,33.7133333,-87.3886111\nSandfort,AL,,32.3380556,-85.2233333\nWest Hollywood,CA,37031,34.0900000,-118.3608333\"; let mut rdr = csv::ReaderBuilder::new().from_reader(data.as_bytes()); let mut wtr = csv::Writer::from_writer(io::stdout()); wtr.write_record(rdr.headers()?)?; for result in rdr.records() { let record = result?; if record.iter().any(|field| field == query) { wtr.write_record(&record)?; } } wtr.flush()?; Ok(())\n}","breadcrumbs":"编解码 » CSV » 基于给定条件来过滤 CSV 记录","id":"302","title":"基于给定条件来过滤 CSV 记录"},"303":{"body":"下面例子展示了如何将 Rust 类型序列化为 CSV。 use std::io; fn main() -> Result<()> { let mut wtr = csv::Writer::from_writer(io::stdout()); wtr.write_record(&[\"Name\", \"Place\", \"ID\"])?; wtr.serialize((\"Mark\", \"Sydney\", 87))?; wtr.serialize((\"Ashley\", \"Dublin\", 32))?; wtr.serialize((\"Akshat\", \"Delhi\", 11))?; wtr.flush()?; Ok(())\n}","breadcrumbs":"编解码 » CSV » 序列化为 CSV","id":"303","title":"序列化为 CSV"},"304":{"body":"下面例子将自定义数据结构通过 serde 序列化 CSV。 use error_chain::error_chain;\nuse serde::Serialize;\nuse std::io; error_chain! { foreign_links { IOError(std::io::Error); CSVError(csv::Error); }\n} #[derive(Serialize)]\nstruct Record<'a> { name: &'a str, place: &'a str, id: u64,\n} fn main() -> Result<()> { let mut wtr = csv::Writer::from_writer(io::stdout()); let rec1 = Record { name: \"Mark\", place: \"Melbourne\", id: 56}; let rec2 = Record { name: \"Ashley\", place: \"Sydney\", id: 64}; let rec3 = Record { name: \"Akshat\", place: \"Delhi\", id: 98}; wtr.serialize(rec1)?; wtr.serialize(rec2)?; wtr.serialize(rec3)?; wtr.flush()?; Ok(())\n}","breadcrumbs":"编解码 » CSV » 使用 serde 序列化为 CSV","id":"304","title":"使用 serde 序列化为 CSV"},"305":{"body":"下面代码将包含有颜色名和十六进制颜色的 CSV 文件转换为包含颜色名和 rgb 颜色。这里使用 csv 包对 CSV 文件进行读写,然后用 serde 进行序列化和反序列化。 #use error_chain::error_chain;\nuse csv::{Reader, Writer};\nuse serde::{de, Deserialize, Deserializer};\nuse std::str::FromStr; #error_chain! {\n# foreign_links {\n# CsvError(csv::Error);\n# ParseInt(std::num::ParseIntError);\n# CsvInnerError(csv::IntoInnerError>>);\n# IO(std::fmt::Error);\n# UTF8(std::string::FromUtf8Error);\n# }\n#} #[derive(Debug)]\nstruct HexColor { red: u8, green: u8, blue: u8,\n} #[derive(Debug, Deserialize)]\nstruct Row { color_name: String, color: HexColor,\n} impl FromStr for HexColor { type Err = Error; fn from_str(hex_color: &str) -> std::result::Result { let trimmed = hex_color.trim_matches('#'); if trimmed.len() != 6 { Err(\"Invalid length of hex string\".into()) } else { Ok(HexColor { red: u8::from_str_radix(&trimmed[..2], 16)?, green: u8::from_str_radix(&trimmed[2..4], 16)?, blue: u8::from_str_radix(&trimmed[4..6], 16)?, }) } }\n} impl<'de> Deserialize<'de> for HexColor { fn deserialize(deserializer: D) -> std::result::Result where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; FromStr::from_str(&s).map_err(de::Error::custom) }\n} fn main() -> Result<()> { let data = \"color_name,color\nred,#ff0000\ngreen,#00ff00\nblue,#0000FF\nperiwinkle,#ccccff\nmagenta,#ff00ff\" .to_owned(); let mut out = Writer::from_writer(vec![]); let mut reader = Reader::from_reader(data.as_bytes()); for result in reader.deserialize::() { let res = result?; out.serialize(( res.color_name, res.color.red, res.color.green, res.color.blue, ))?; } let written = String::from_utf8(out.into_inner()?)?; assert_eq!(Some(\"magenta,255,0,255\"), written.lines().last()); println!(\"{}\", written); Ok(())\n}","breadcrumbs":"编解码 » CSV » CSV 列转换","id":"305","title":"CSV 列转换"},"306":{"body":"","breadcrumbs":"编解码 » 结构化数据 » 结构化数据","id":"306","title":"结构化数据"},"307":{"body":"serde_json 是一个高性能的 JSON 包,它支持我们在不声明结构体的情况下,去解析 JSON。 use serde_json::json;\nuse serde_json::{Value, Error}; fn main() -> Result<(), Error> { let j = r#\"{ \"userid\": 103609, \"verified\": true, \"access_privileges\": [ \"user\", \"admin\" ] }\"#; let parsed: Value = serde_json::from_str(j)?; let expected = json!({ \"userid\": 103609, \"verified\": true, \"access_privileges\": [ \"user\", \"admin\" ] }); assert_eq!(parsed, expected); Ok(())\n}","breadcrumbs":"编解码 » 结构化数据 » 序列和反序列非结构化的JSON","id":"307","title":"序列和反序列非结构化的JSON"},"308":{"body":"toml 包可以将 TOML 文件的内容解析为一个 toml::Value 值,该值能代表任何合法的 TOML 数据。 use toml::{Value, de::Error}; fn main() -> Result<(), Error> { let toml_content = r#\" [package] name = \"your_package\" version = \"0.1.0\" authors = [\"You! \"] [dependencies] serde = \"1.0\" \"#; let package_info: Value = toml::from_str(toml_content)?; assert_eq!(package_info[\"dependencies\"][\"serde\"].as_str(), Some(\"1.0\")); assert_eq!(package_info[\"package\"][\"name\"].as_str(), Some(\"your_package\")); Ok(())\n} 还可以配合 serde 将 TOML 解析到我们自定义的结构体中: use serde::Deserialize; use toml::de::Error;\nuse std::collections::HashMap; #[derive(Deserialize)]\nstruct Config { package: Package, dependencies: HashMap,\n} #[derive(Deserialize)]\nstruct Package { name: String, version: String, authors: Vec,\n} fn main() -> Result<(), Error> { let toml_content = r#\" [package] name = \"your_package\" version = \"0.1.0\" authors = [\"You! \"] [dependencies] serde = \"1.0\" \"#; let package_info: Config = toml::from_str(toml_content)?; assert_eq!(package_info.package.name, \"your_package\"); assert_eq!(package_info.package.version, \"0.1.0\"); assert_eq!(package_info.package.authors, vec![\"You! \"]); assert_eq!(package_info.dependencies[\"serde\"], \"1.0\"); Ok(())\n}","breadcrumbs":"编解码 » 结构化数据 » 解析 TOML 文件","id":"308","title":"解析 TOML 文件"},"309":{"body":"byteorder 在自行接收或发送网络字节流时会非常有用( 除非性能要求高,否则还是建议使用 JSON 等数据协议,不要自己做字节流解析 )。 use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};\nuse std::io::Error; #[derive(Default, PartialEq, Debug)]\nstruct Payload { kind: u8, value: u16,\n} fn main() -> Result<(), Error> { let original_payload = Payload::default(); let encoded_bytes = encode(&original_payload)?; let decoded_payload = decode(&encoded_bytes)?; assert_eq!(original_payload, decoded_payload); Ok(())\n} fn encode(payload: &Payload) -> Result, Error> { let mut bytes = vec![]; bytes.write_u8(payload.kind)?; bytes.write_u16::(payload.value)?; Ok(bytes)\n} fn decode(mut bytes: &[u8]) -> Result { let payload = Payload { kind: bytes.read_u8()?, value: bytes.read_u16::()?, }; Ok(payload)\n}","breadcrumbs":"编解码 » 结构化数据 » 使用小端字节序来读写整数","id":"309","title":"使用小端字节序来读写整数"},"31":{"body":"AppFlowy 是 Notion 的开源实现,使用Rust和Flutter进行开发,用于用户文档和数据的管理,支持丰富的自定义特性。","breadcrumbs":"Rust 明星项目 » AppFlowy","id":"31","title":"AppFlowy"},"310":{"body":"","breadcrumbs":"文件操作 » 文件读写 » 文件读写","id":"310","title":"文件读写"},"311":{"body":"use std::fs::File;\nuse std::io::{Write, BufReader, BufRead, Error}; fn main() -> Result<(), Error> { let path = \"lines.txt\"; // 创建文件 let mut output = File::create(path)?; // 写入三行内容 write!(output, \"Rust\\n💖\\nFun\")?; let input = File::open(path)?; let buffered = BufReader::new(input); // 迭代文件中的每一行内容,line 是字符串 for line in buffered.lines() { println!(\"{}\", line?); } Ok(())\n}","breadcrumbs":"文件操作 » 文件读写 » 迭代文件中的内容行","id":"311","title":"迭代文件中的内容行"},"312":{"body":"same_file 可以帮我们识别两个文件是否是相同的。 use same_file::Handle;\nuse std::fs::File;\nuse std::io::{BufRead, BufReader, Error, ErrorKind};\nuse std::path::Path; fn main() -> Result<(), Error> { let path_to_read = Path::new(\"new.txt\"); // 从标准输出上获取待写入的文件名 let stdout_handle = Handle::stdout()?; // 将待写入的文件名跟待读取的文件名进行比较 let handle = Handle::from_path(path_to_read)?; if stdout_handle == handle { return Err(Error::new( ErrorKind::Other, \"You are reading and writing to the same file\", )); } else { let file = File::open(&path_to_read)?; let file = BufReader::new(file); for (num, line) in file.lines().enumerate() { println!(\"{} : {}\", num, line?.to_uppercase()); } } Ok(())\n} 以下代码会报错,因为待写入的文件名也是 new.txt ,跟待读取的文件名相同 cargo run >> ./new.txt","breadcrumbs":"文件操作 » 文件读写 » 避免对同一个文件进行读写","id":"312","title":"避免对同一个文件进行读写"},"313":{"body":"memmap 能创建一个文件的内存映射( memory map ),然后模拟一些非顺序读。 使用内存映射,意味着你将相关的索引加载到内存中,而不是通过 seek 的方式去访问文件。 Mmap::map 函数会假定待映射的文件不会同时被其它进程修改。 use memmap::Mmap;\nuse std::fs::File;\nuse std::io::{Write, Error}; fn main() -> Result<(), Error> { write!(File::create(\"content.txt\")?, \"My hovercraft is full of eels!\")?; let file = File::open(\"content.txt\")?; let map = unsafe { Mmap::map(&file)? }; let random_indexes = [0, 1, 2, 19, 22, 10, 11, 29]; assert_eq!(&map[3..13], b\"hovercraft\"); let random_bytes: Vec = random_indexes.iter() .map(|&idx| map[idx]) .collect(); assert_eq!(&random_bytes[..], b\"My loaf!\"); Ok(())\n}","breadcrumbs":"文件操作 » 文件读写 » 使用内存映射访问文件","id":"313","title":"使用内存映射访问文件"},"314":{"body":"","breadcrumbs":"文件操作 » 目录访问 » 目录访问","id":"314","title":"目录访问"},"315":{"body":"通过遍历读取目录中文件的 Metadata::modified 属性,来获取目标文件名列表。 use error_chain::error_chain; use std::{env, fs}; error_chain! { foreign_links { Io(std::io::Error); SystemTimeError(std::time::SystemTimeError); }\n} fn main() -> Result<()> { let current_dir = env::current_dir()?; println!( \"Entries modified in the last 24 hours in {:?}:\", current_dir ); for entry in fs::read_dir(current_dir)? { let entry = entry?; let path = entry.path(); let metadata = fs::metadata(&path)?; let last_modified = metadata.modified()?.elapsed()?.as_secs(); if last_modified < 24 * 3600 && metadata.is_file() { println!( \"Last modified: {:?} seconds, is read only: {:?}, size: {:?} bytes, filename: {:?}\", last_modified, metadata.permissions().readonly(), metadata.len(), path.file_name().ok_or(\"No filename\")? ); } } Ok(())\n}","breadcrumbs":"文件操作 » 目录访问 » 获取24小时内被修改过的文件","id":"315","title":"获取24小时内被修改过的文件"},"316":{"body":"使用 same_file::is_same_file 可以检查给定路径的 loops,loop 可以通过以下方式创建: mkdir -p /tmp/foo/bar/baz\nln -s /tmp/foo/ /tmp/foo/bar/baz/qux use std::io;\nuse std::path::{Path, PathBuf};\nuse same_file::is_same_file; fn contains_loop>(path: P) -> io::Result> { let path = path.as_ref(); let mut path_buf = path.to_path_buf(); while path_buf.pop() { if is_same_file(&path_buf, path)? { return Ok(Some((path_buf, path.to_path_buf()))); } else if let Some(looped_paths) = contains_loop(&path_buf)? { return Ok(Some(looped_paths)); } } return Ok(None);\n} fn main() { assert_eq!( contains_loop(\"/tmp/foo/bar/baz/qux/bar/baz\").unwrap(), Some(( PathBuf::from(\"/tmp/foo\"), PathBuf::from(\"/tmp/foo/bar/baz/qux\") )) );\n}","breadcrumbs":"文件操作 » 目录访问 » 获取给定路径的 loops","id":"316","title":"获取给定路径的 loops"},"317":{"body":"walkdir 可以帮助我们遍历指定的目录。 use std::collections::HashMap;\nuse walkdir::WalkDir; fn main() { let mut filenames = HashMap::new(); // 遍历当前目录 for entry in WalkDir::new(\".\") .into_iter() .filter_map(Result::ok) .filter(|e| !e.file_type().is_dir()) { let f_name = String::from(entry.file_name().to_string_lossy()); let counter = filenames.entry(f_name.clone()).or_insert(0); *counter += 1; if *counter == 2 { println!(\"{}\", f_name); } }\n}","breadcrumbs":"文件操作 » 目录访问 » 递归查找重复的文件名","id":"317","title":"递归查找重复的文件名"},"318":{"body":"下面的代码通过 walkdir 来查找当前目录中最近一天内发生过修改的所有文件。 follow_links 为 true 时,那软链接会被当成正常的文件或目录一样对待,也就是说软链接指向的文件或目录也会被访问和检查。若软链接指向的目标不存在或它是一个 loops,就会导致错误的发生。 #use error_chain::error_chain; use walkdir::WalkDir; #error_chain! {\n# foreign_links {\n# WalkDir(walkdir::Error);\n# Io(std::io::Error);\n# SystemTime(std::time::SystemTimeError);\n# }\n#} fn main() -> Result<()> { for entry in WalkDir::new(\".\") .follow_links(true) .into_iter() .filter_map(|e| e.ok()) { let f_name = entry.file_name().to_string_lossy(); let sec = entry.metadata()?.modified()?; if f_name.ends_with(\".json\") && sec.elapsed()?.as_secs() < 86400 { println!(\"{}\", f_name); } } Ok(())\n}","breadcrumbs":"文件操作 » 目录访问 » 递归查找满足条件的所有文件","id":"318","title":"递归查找满足条件的所有文件"},"319":{"body":"下面例子使用 walkdir 来遍历一个目录,同时跳过隐藏文件 is_not_hidden。 use walkdir::{DirEntry, WalkDir}; fn is_not_hidden(entry: &DirEntry) -> bool { entry .file_name() .to_str() .map(|s| entry.depth() == 0 || !s.starts_with(\".\")) .unwrap_or(false)\n} fn main() { WalkDir::new(\".\") .into_iter() .filter_entry(|e| is_not_hidden(e)) .filter_map(|v| v.ok()) .for_each(|x| println!(\"{}\", x.path().display()));\n}","breadcrumbs":"文件操作 » 目录访问 » 遍历目录跳过隐藏文件","id":"319","title":"遍历目录跳过隐藏文件"},"32":{"body":"bevy 是一个数据驱动的游戏引擎,支持2D和3D图形开发,优点是社区活跃、更新快、模块化设计优秀、性能高,缺点是还处于快速开发中,并不适合生产使用。 同时bevy的文档齐全,官方示例很多,非常适合学习和使用。","breadcrumbs":"Rust 明星项目 » Bevy","id":"32","title":"Bevy"},"320":{"body":"递归访问的深度可以使用 WalkDir::min_depth 和 WalkDir::max_depth 来控制。 use walkdir::WalkDir; fn main() { let total_size = WalkDir::new(\".\") .min_depth(1) .max_depth(3) .into_iter() .filter_map(|entry| entry.ok()) .filter_map(|entry| entry.metadata().ok()) .filter(|metadata| metadata.is_file()) .fold(0, |acc, m| acc + m.len()); println!(\"Total size: {} bytes.\", total_size);\n}","breadcrumbs":"文件操作 » 目录访问 » 递归计算给定深度的文件大小","id":"320","title":"递归计算给定深度的文件大小"},"321":{"body":"例子中使用了 glob 包,其中的 ** 代表当前目录及其所有子目录,例如,/media/**/*.png 代表在 media 和它的所有子目录下查找 png 文件. #use error_chain::error_chain; use glob::glob; #error_chain! {\n# foreign_links {\n# Glob(glob::GlobError);\n# Pattern(glob::PatternError);\n# }\n#} fn main() -> Result<()> { for entry in glob(\"**/*.png\")? { println!(\"{}\", entry?.display()); } Ok(())\n}","breadcrumbs":"文件操作 » 目录访问 » 递归查找所有 png 文件","id":"321","title":"递归查找所有 png 文件"},"322":{"body":"glob_with 函数可以按照给定的正则表达式进行查找,同时还能使用选项来控制一些匹配设置。 use error_chain::error_chain;\nuse glob::{glob_with, MatchOptions}; error_chain! { foreign_links { Glob(glob::GlobError); Pattern(glob::PatternError); }\n} fn main() -> Result<()> { let options = MatchOptions { case_sensitive: false, ..Default::default() }; for entry in glob_with(\"/media/img_[0-9]*.png\", options)? { println!(\"{}\", entry?.display()); } Ok(())\n}","breadcrumbs":"文件操作 » 目录访问 » 查找满足给定正则的所有文件且忽略文件名大小写","id":"322","title":"查找满足给定正则的所有文件且忽略文件名大小写"},"323":{"body":"","breadcrumbs":"内存管理 » 全局变量 » 全局变量","id":"323","title":"全局变量"},"324":{"body":"下面的例子,我们将使用 lazy_static 声明一个在运行期初始化( 懒求值 )的 Hashmap,它会被求值一次,然后保存在一个全局的 static 引用之后。 use lazy_static::lazy_static;\nuse std::collections::HashMap; lazy_static! { static ref PRIVILEGES: HashMap<&'static str, Vec<&'static str>> = { let mut map = HashMap::new(); map.insert(\"James\", vec![\"user\", \"admin\"]); map.insert(\"Jim\", vec![\"user\"]); map };\n} fn show_access(name: &str) { let access = PRIVILEGES.get(name); println!(\"{}: {:?}\", name, access);\n} fn main() { let access = PRIVILEGES.get(\"James\"); println!(\"James: {:?}\", access); show_access(\"Jim\");\n}","breadcrumbs":"内存管理 » 全局变量 » 使用 lazy_static 在运行期初始化全局变量","id":"324","title":"使用 lazy_static 在运行期初始化全局变量"},"325":{"body":"","breadcrumbs":"网络协议 » TCP/IP » TCP/IP","id":"325","title":"TCP/IP"},"326":{"body":"以下代码会监听指定的 TCP 端口,并接收一条外部进入的 TCP 连接,然后将读取到的一条信息输出到标准输出( println! )。 use std::net::{SocketAddrV4, Ipv4Addr, TcpListener};\nuse std::io::{Read, Error}; fn main() -> Result<(), Error> { let loopback = Ipv4Addr::new(127, 0, 0, 1); let socket = SocketAddrV4::new(loopback, 0); let listener = TcpListener::bind(socket)?; let port = listener.local_addr()?; println!(\"Listening on {}, access this port to end the program\", port); let (mut tcp_stream, addr) = listener.accept()?; //block until requested println!(\"Connection received! {:?} is sending data.\", addr); let mut input = String::new(); let _ = tcp_stream.read_to_string(&mut input)?; println!(\"{:?} says {}\", addr, input); Ok(())\n}","breadcrumbs":"网络协议 » TCP/IP » 监听 TCP 端口","id":"326","title":"监听 TCP 端口"},"327":{"body":"@todo","breadcrumbs":"网络协议 » TCP/IP » 循环接收进入的 TCP 连接","id":"327","title":"循环接收进入的 TCP 连接"},"328":{"body":"","breadcrumbs":"文本处理 » 正则表达式 » 正则表达式","id":"328","title":"正则表达式"},"329":{"body":"下面代码使用 regex 包来验证邮件格式的正确性,然后提取出 @ 符号前的所有内容。 use lazy_static::lazy_static; use regex::Regex; fn extract_login(input: &str) -> Option<&str> { lazy_static! { static ref RE: Regex = Regex::new(r\"(?x) ^(?P[^@\\s]+)@ ([[:word:]]+\\.)* [[:word:]]+$ \").unwrap(); } RE.captures(input).and_then(|cap| { cap.name(\"login\").map(|login| login.as_str()) })\n} fn main() { assert_eq!(extract_login(r\"I❤email@example.com\"), Some(r\"I❤email\")); assert_eq!( extract_login(r\"sdf+sdsfsd.as.sdsd@jhkk.d.rl\"), Some(r\"sdf+sdsfsd.as.sdsd\") ); assert_eq!(extract_login(r\"More@Than@One@at.com\"), None); assert_eq!(extract_login(r\"Not an email@email\"), None);\n}","breadcrumbs":"文本处理 » 正则表达式 » 验证邮件格式并取出 @ 前的信息","id":"329","title":"验证邮件格式并取出 @ 前的信息"},"33":{"body":"actix-web 是全世界最快的web框架之一,甚至可以把之一去掉,因为排在它前面的看上去像是一个专为跑分而生的轻量级框架,而actix-web可是功能相当多的! 下面给出actix和Go语言Gin框架的性能对比:","breadcrumbs":"Rust 明星项目 » actix-web","id":"33","title":"actix-web"},"330":{"body":"例子对标签进行提取、排序和去重。需要注意,下面的标签仅仅是拉丁字母的,如果你要支持更多的字母,可以参考下 Twitter 的正则语法 ,友情提示,复杂的多! use lazy_static::lazy_static; use regex::Regex;\nuse std::collections::HashSet; fn extract_hashtags(text: &str) -> HashSet<&str> { lazy_static! { static ref HASHTAG_REGEX : Regex = Regex::new( r\"\\#[a-zA-Z][0-9a-zA-Z_]*\" ).unwrap(); } HASHTAG_REGEX.find_iter(text).map(|mat| mat.as_str()).collect()\n} fn main() { let tweet = \"Hey #world, I just got my new #dog, say hello to Till. #dog #forever #2 #_ \"; let tags = extract_hashtags(tweet); assert!(tags.contains(\"#dog\") && tags.contains(\"#forever\") && tags.contains(\"#world\")); assert_eq!(tags.len(), 3);\n}","breadcrumbs":"文本处理 » 正则表达式 » 从文本中提出 # 开头的标签","id":"330","title":"从文本中提出 # 开头的标签"},"331":{"body":"[Regex::captures_iter] 可以对字符串型文本进行处理,以获取文本中的多个手机号。下面的例子适用于美国的号码。 #use error_chain::error_chain; use regex::Regex;\nuse std::fmt; #error_chain!{\n# foreign_links {\n# Regex(regex::Error);\n# Io(std::io::Error);\n# }\n#} struct PhoneNumber<'a> { area: &'a str, exchange: &'a str, subscriber: &'a str,\n} impl<'a> fmt::Display for PhoneNumber<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, \"1 ({}) {}-{}\", self.area, self.exchange, self.subscriber) }\n} fn main() -> Result<()> { let phone_text = \" +1 505 881 9292 (v) +1 505 778 2212 (c) +1 505 881 9297 (f) (202) 991 9534 Alex 5553920011 1 (800) 233-2010 1.299.339.1020\"; let re = Regex::new( r#\"(?x) (?:\\+?1)? # Country Code Optional [\\s\\.]? (([2-9]\\d{2})|\\(([2-9]\\d{2})\\)) # Area Code [\\s\\.\\-]? ([2-9]\\d{2}) # Exchange Code [\\s\\.\\-]? (\\d{4}) # Subscriber Number\"#, )?; let phone_numbers = re.captures_iter(phone_text).filter_map(|cap| { let groups = (cap.get(2).or(cap.get(3)), cap.get(4), cap.get(5)); match groups { (Some(area), Some(ext), Some(sub)) => Some(PhoneNumber { area: area.as_str(), exchange: ext.as_str(), subscriber: sub.as_str(), }), _ => None, } }); assert_eq!( phone_numbers.map(|m| m.to_string()).collect::>(), vec![ \"1 (505) 881-9292\", \"1 (505) 778-2212\", \"1 (505) 881-9297\", \"1 (202) 991-9534\", \"1 (555) 392-0011\", \"1 (800) 233-2010\", \"1 (299) 339-1020\", ] ); Ok(())\n}","breadcrumbs":"文本处理 » 正则表达式 » 从文本中提取出所有手机号","id":"331","title":"从文本中提取出所有手机号"},"332":{"body":"例子的目标是过滤出包含 \"version X.X.X\"、以 443 结尾的 IP 地址和特别的警告的日志行。 值得注意的是,由于在正则中反斜杠非常常见,因此使用 r#\"\" 形式的原生字符串对于开发者和使用者都更加友好。 #use error_chain::error_chain; use std::fs::File;\nuse std::io::{BufReader, BufRead};\nuse regex::RegexSetBuilder; #error_chain! {\n# foreign_links {\n# Io(std::io::Error);\n# Regex(regex::Error);\n# }\n#} fn main() -> Result<()> { let log_path = \"application.log\"; let buffered = BufReader::new(File::open(log_path)?); let set = RegexSetBuilder::new(&[ r#\"version \"\\d\\.\\d\\.\\d\"\"#, r#\"\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}:443\"#, r#\"warning.*timeout expired\"#, ]).case_insensitive(true) .build()?; buffered .lines() .filter_map(|line| line.ok()) .filter(|line| set.is_match(line.as_str())) .for_each(|x| println!(\"{}\", x)); Ok(())\n}","breadcrumbs":"文本处理 » 正则表达式 » 通过多个正则来过滤日志文件","id":"332","title":"通过多个正则来过滤日志文件"},"333":{"body":"下面代码将标准的 ISO 8601 YYYY-MM-DD 日期模式替换成带有斜杠的美式英语日期。例如 2013-01-15 -> 01/15/2013。 use lazy_static::lazy_static; use std::borrow::Cow;\nuse regex::Regex; fn reformat_dates(before: &str) -> Cow { lazy_static! { static ref ISO8601_DATE_REGEX : Regex = Regex::new( r\"(?P\\d{4})-(?P\\d{2})-(?P\\d{2})\" ).unwrap(); } ISO8601_DATE_REGEX.replace_all(before, \"$m/$d/$y\")\n} fn main() { let before = \"2012-03-14, 2013-01-15 and 2014-07-05\"; let after = reformat_dates(before); assert_eq!(after, \"03/14/2012, 01/15/2013 and 07/05/2014\");\n}","breadcrumbs":"文本处理 » 正则表达式 » 将文本中所有的指定模式替换成另外一种模式","id":"333","title":"将文本中所有的指定模式替换成另外一种模式"},"334":{"body":"","breadcrumbs":"文本处理 » 字符串解析 » 字符串解析","id":"334","title":"字符串解析"},"335":{"body":"unicode-segmentation 包的 UnicodeSegmentation::graphemes 函数可以将 UTF-8 字符串收集成一个 Unicode 字符组成的数组。这样我们就可以通过索引的方式来访问对应的字符了。 use unicode_segmentation::UnicodeSegmentation; fn main() { let name = \"José Guimarães\\r\\n\"; let graphemes = UnicodeSegmentation::graphemes(name, true) .collect::>(); assert_eq!(graphemes[3], \"é\");\n}","breadcrumbs":"文本处理 » 字符串解析 » 访问 Unicode 字符","id":"335","title":"访问 Unicode 字符"},"336":{"body":"为我们的 RGB 结构体实现 FromStr 特征后,就可以将一个十六进制的颜色表示字符串转换成 RGB 结构体。 use std::str::FromStr; #[derive(Debug, PartialEq)]\nstruct RGB { r: u8, g: u8, b: u8,\n} impl FromStr for RGB { type Err = std::num::ParseIntError; // 将十六进制的颜色码解析为 `RGB` 的实例 fn from_str(hex_code: &str) -> Result { // u8::from_str_radix(src: &str, radix: u32) 将一个字符串切片按照指定的基数转换为 u8 类型 let r: u8 = u8::from_str_radix(&hex_code[1..3], 16)?; let g: u8 = u8::from_str_radix(&hex_code[3..5], 16)?; let b: u8 = u8::from_str_radix(&hex_code[5..7], 16)?; Ok(RGB { r, g, b }) }\n} fn main() { let code: &str = &r\"#fa7268\"; match RGB::from_str(code) { Ok(rgb) => { println!( r\"The RGB color code is: R: {} G: {} B: {}\", rgb.r, rgb.g, rgb.b ); } Err(_) => { println!(\"{} is not a valid color hex code!\", code); } } // 测试 from_str 是否按照预期工作 assert_eq!( RGB::from_str(&r\"#fa7268\").unwrap(), RGB { r: 250, g: 114, b: 104 } );\n}","breadcrumbs":"文本处理 » 字符串解析 » 为自定义结构体实现 FromStr 特征","id":"336","title":"为自定义结构体实现 FromStr 特征"},"337":{"body":"@todo","breadcrumbs":"文本处理 » 字符串解析 » 实现 Display 特征","id":"337","title":"实现 Display 特征"},"34":{"body":"iced 是一个跨平台GUI库,具有简单易用、模块化设计、响应式布局等优点。","breadcrumbs":"Rust 明星项目 » iced","id":"34","title":"iced"},"35":{"body":"cube.js 是一个数据分析API平台,可以用于构建内部的BI或为现有的应用增加客户数据统计等功能,使用Rust和Typescript构建。","breadcrumbs":"Rust 明星项目 » cube.js","id":"35","title":"cube.js"},"36":{"body":"wasmer 是业界领先的WASM运行时,支持WASI和Emscripten。 $ wasmer qjs.wasm\nQuickJS - Type \"\\h\" for help\nqjs > const i = 1 + 2;\nqjs > console.log(\"hello \" + i);\nhello 3","breadcrumbs":"Rust 明星项目 » wasmer","id":"36","title":"wasmer"},"37":{"body":"tikv 相信大家都已知道,tidb的底层存储服务,国人之光项目,在数据之外,还做了大量的技术知识普及工作,值得敬佩! tikv是分布式KV数据库,支持分布式事务。","breadcrumbs":"Rust 明星项目 » tikv","id":"37","title":"tikv"},"38":{"body":"ruffle 是用Rust写的Flash Player模拟器,同时支持桌面端和Web端,其中后者通过WASM提供支持。","breadcrumbs":"Rust 明星项目 » ruffle","id":"38","title":"ruffle"},"39":{"body":"rustdesk 是国内团队开发的一款远程桌面软件。","breadcrumbs":"Rust 明星项目 » rustdesk","id":"39","title":"rustdesk"},"4":{"body":"多线程 Web/HTTP , SQL客户端 , NoSql客户端 , 网络通信协议 , 异步网络编程 服务发现 , 消息队列 , 搜索引擎 编解码 , Email , 常用正则模版 日志监控 , 代码Debug , 性能优化","breadcrumbs":"日常开发常用库 » 目录索引","id":"4","title":"目录索引"},"40":{"body":"[RustPython]是使用Rust实现的Python解释器, 支持Python3(CPython >= 3.9.0)。 大家可以通过官方提供的 在线网址 进行尝试。","breadcrumbs":"Rust 明星项目 » RustPython","id":"40","title":"RustPython"},"41":{"body":"vector 是一个性能很高的数据采集agent,采集本地的日志、监控等数据,发送到远程的kafka、jaeger等数据下沉端,它最大的优点就是能从多种数据源(包括Opentelemetry)收集数据,然后推送到多个数据处理或者存储等下沉端。","breadcrumbs":"Rust 明星项目 » vector","id":"41","title":"vector"},"42":{"body":"mdbbok 可以基于markdown文件自动创建在线电子书,非常简单好用,目前的问题就是缺乏章节内部的目录跳转和中文搜索。","breadcrumbs":"Rust 明星项目 » mdbook","id":"42","title":"mdbook"},"43":{"body":"zola 是一个静态网站生成器,类似hugo。","breadcrumbs":"Rust 明星项目 » zola","id":"43","title":"zola"},"44":{"body":"gitui 是一个奇快无比的Git终端UI,无需浏览器即可使用。","breadcrumbs":"Rust 明星项目 » gitui","id":"44","title":"gitui"},"45":{"body":"solana 是知名的区块链平台,快速、安全、去中心化,还自带应用市场。","breadcrumbs":"Rust 明星项目 » solana","id":"45","title":"solana"},"46":{"body":"ripgrep 是一个性能极高的现代化grep实现,后者是Unix/Linux下的内置文件搜索工具。该项目是Rust的明星项目,一个是因为性能极其的高,另一个就是源代码质量很高,值得学习, 同时Vscode使用它作为内置的搜索引擎。 从功能来说,除了全面支持grep的功能外,repgre支持使用正则递归搜索指定的文件目录,默认使用.gitignore对指定的文件进行忽略。","breadcrumbs":"Rust 明星项目 » ripgrep","id":"46","title":"ripgrep"},"47":{"body":"citybound 是一个多人在线模拟游戏,使用Rust + WASM + JS开发。","breadcrumbs":"Rust 明星项目 » citybound","id":"47","title":"citybound"},"48":{"body":"bottlerocket 是一个基于Linux的操作系统,它的目标是为容器提供宿主环境。","breadcrumbs":"Rust 明星项目 » bottlerocket","id":"48","title":"bottlerocket"},"49":{"body":"lemmy 是一个reddit克隆,可以通过连接聚合的方式来构建社区,支持桌面和移动端。","breadcrumbs":"Rust 明星项目 » lemmy","id":"49","title":"lemmy"},"5":{"body":"HTTP客户端 reqwest 一个简单又强大的HTTP客户端,reqwest是目前使用最多的HTTP库 Web框架 axum 基于Tokio和Hyper打造,模块化设计较好,目前口碑很好,值得使用Ergonomic and modular web framework built with Tokio, Tower, and Hyper Rocket 功能强大,API简单的Web框架,但是主要开发者目前因为个人原因无法进行后续开发,未来存在不确定性 actix-web 性能极高的Web框架,就是团队内部有些问题,未来存在一定的不确定性 总体来说,上述三个web框架都有很深的用户基础,其实都可以选用,如果让我推荐,顺序如下: axum > Rocket > actix-web。 不过如果你不需要多么完善的web功能,只需要一个性能极高的http库,那么actix-web是非常好的选择,它的性能非常非常非常高!","breadcrumbs":"日常开发常用库 » Web/HTTP","id":"5","title":"Web/HTTP"},"50":{"body":"tantivy 是Rust实现的本地搜索库,功能对标lucene,如果你不需要分布式,那么引入tantivy作为自己本地Rust服务的一个搜索,是相当不错的选择,该库作者一直很活跃,而且最近还创立了搜索引擎公司,感觉大有作为. 该库的优点在于纯Rust实现,性能高(lucene的2-3倍),资源占用低(对比java自然不是一个数量级),社区活跃。","breadcrumbs":"Rust 明星项目 » tantivy","id":"50","title":"tantivy"},"51":{"body":"sled 是本地嵌入式的数据库。 let tree = sled::open(\"/tmp/welcome-to-sled\")?; // insert and get, similar to std's BTreeMap\nlet old_value = tree.insert(\"key\", \"value\")?; assert_eq!( tree.get(&\"key\")?, Some(sled::IVec::from(\"value\")),\n); // range queries\nfor kv_result in tree.range(\"key_1\"..\"key_9\") {} // deletion\nlet old_value = tree.remove(&\"key\")?; // atomic compare and swap\ntree.compare_and_swap( \"key\", Some(\"current_value\"), Some(\"new_value\"),\n)?; // block until all operations are stable on disk\n// (flush_async also available to get a Future)\ntree.flush()?;","breadcrumbs":"Rust 明星项目 » sled","id":"51","title":"sled"},"52":{"body":"Redox 是一个Unix风格的微内核操作系统,使用Rust实现。redox的目标是安全、快速、免费、可用,它在内核设计上借鉴了很多优秀的内核,例如:SeL4, MINIX, Plan 9和BSD。 但redox不仅仅是一个内核,它还是一个功能齐全的操作系统,提供了操作系统该有的功能,例如:内存分配器、文件系统、显示管理、核心工具等等。你可以大概认为它是一个GNU或BSD生态,但是是通过一门现代化、内存安全的语言实现的。 不过据我仔细观察,redox目前的开发进度不是很活跃,不知道发生了什么,未来若有新的发现会在这里进行更新 - Sunface","breadcrumbs":"Rust 明星项目 » redox","id":"52","title":"redox"},"53":{"body":"youki 是一个容器运行时,实现了OCI标准,性能非常好的同时具备非常高的安全性, 目前来说,它的性能跟crun差不多,比runc快50%以上。","breadcrumbs":"Rust 明星项目 » youki","id":"53","title":"youki"},"54":{"body":"sixtyfps 是一个GUI工具集,同时适用于嵌入式系统、桌面系统、移动端、浏览器(WASM),支持使用多种语言进行开发,背后有商业公司的支持,未来前景看好。","breadcrumbs":"Rust 明星项目 » sixtyfps","id":"54","title":"sixtyfps"},"55":{"body":"wasmtime 是一个为WASM设计的JIT风格的独立运行时,支持WASI。 fn main() { println!(\"Hello, world!\");\n} $ rustup target add wasm32-wasi\n$ rustc hello.rs --target wasm32-wasi\n$ wasmtime hello.wasm\nHello, w","breadcrumbs":"Rust 明星项目 » wasmtime","id":"55","title":"wasmtime"},"56":{"body":"polkadot 是知名的区块链平台,它是从 Substrate 抽离出来,后者是下一代区块链开发框架。","breadcrumbs":"Rust 明星项目 » polkadot","id":"56","title":"polkadot"},"57":{"body":"lapce 是一款性能极高、功能强大、基于wgpu渲染的代码编辑器,基于Xi-Editor开发,后者Xi-Editor曾经也红极一时,可惜不再维护了,但是依然非常适合做一个编辑器内核。","breadcrumbs":"Rust 明星项目 » lapce","id":"57","title":"lapce"},"58":{"body":"rust-gpu 的目标是让Rust成为GPU编程的第一梯队语言,由大名鼎鼎的Embark公司开发,后台较硬。 如果需要通用的GPU编程,选它就对了。","breadcrumbs":"Rust 明星项目 » rust-gpu","id":"58","title":"rust-gpu"},"59":{"body":"Javascript是目前全世界使用最广的语言(TIOBE排行榜比较迷,JS并没有排在第一位,我个人并不认同它的排名)。在过去这么多年中,围绕着Javascript已经建立了庞大的基础设施生态:例如使用webpack来将多个js文件打包成一个;使用Babel允许你用现代化的js语法编写兼容旧浏览器的代码;使用Eslint帮助开发找出代码中潜在的问题,类似cargo clippy。 以上的种种都在帮助js成为更好的语言和工具,它们是Web应用程序得以顺利、高效的开发和运行的基石。这些工具往往使用Javascript语言编写,一般来说,是没有问题的,但是在某些时候,可能会存在性能上的瓶颈或者安全隐患,因此阴差阳错、机缘巧合下,Rust成为了一个搅局者。","breadcrumbs":"使用 Rust 增强 JS » 使用Rust增强Javascript","id":"59","title":"使用Rust增强Javascript"},"6":{"body":"日志 [ crates.io ] [ github ] tokio-rs/tracing 强大的日志框架,同时还支持OpenTelemetry格式,无缝打通未来的监控 rust-lang/log 官方日志库,事实上的API标准, 但是三方库未必遵循 estk/log4rs 模仿JAVA logback和log4j实现的日志库, 可配置性较强 在其它文章中,也许会推荐slog,但是我们不推荐,一个是因为近半年未更新,一个是slog自己也推荐使用tracing。 监控 OpenTelemetry OpenTelemetry是现在非常火的可观测性解决方案,提供了协议、API、SDK等核心工具,用于收集监控数据,最后将这些metrics/logs/traces数据写入到prometheus, jaeger等监控平台中。最主要是,它后台很硬,后面有各大公司作为背书,未来非常看好! vectordotdev/vector 一个性能很高的数据采集agent,采集本地的日志、监控等数据,发送到远程的kafka、jaeger等数据下沉端,它最大的优点就是能从多种数据源(包括Opentelemetry)收集数据,然后推送到多个数据处理或者存储等下沉端。","breadcrumbs":"日常开发常用库 » 日志监控","id":"6","title":"日志监控"},"60":{"body":"","breadcrumbs":"使用 Rust 增强 JS » Javascript基建库","id":"60","title":"Javascript基建库"},"61":{"body":"首先出场的自然是咖位最重的之一,可以说正是因为deno和swc的横空出世,才让一堆观望的大神对于Rust实现Javascript基建有了更强的信心。 deno是node半逆转后的字序,从此可以看出deno是Node.js的替代,它的目标是为Typescript/Javascript提供一个更现代化、更安全、更强大 的运行时,同时内置了很多强大的工具,可以用于打包、编译成可执行文件、文档、测试、lint等。 值得一提的是,deno的不少工具都使用了swc进行建造,包括代码审查、格式化、文档生成等。 通过包引入的方式来对比下deno和node,大家可以自己品味下。 // node\nconst koa = require(\"koa\" );\ncost logger = require(\"@adesso/logger\") // deno\nimport { Application } from \"https://deno.land/x/oak/mod.ts\";\nimport { Logger } from \"https://adesso.de/lib/logger.ts\"","breadcrumbs":"使用 Rust 增强 JS » deno","id":"61","title":"deno"},"62":{"body":"swc 是Typescript/Javascript编译器,它可以用来编译、压缩和打包JS,同时支持使用插件进行扩展,例如做代码变换等。 swc目前正在被一些知名项目所使用,包括Next.js,Parcel和Deno,还有些著名的公司也在使用它,例如Vercel、字节跳动、腾讯等。 它的性能非常非常高,官方号称,在单线程下比Babel快20倍,在4核心下比Babel快70倍! 几个使用案例: nextjs 12 , 通过使用swc获得了更好的扩展性、性能以及wasm的支持,其中性能方面提升了3倍刷新速度、5倍打包速度 Parcel ,通过使用swc改善了10倍的性能 官方还提供了一个在线运行的 demo ,功能齐全,可以试试。","breadcrumbs":"使用 Rust 增强 JS » swc","id":"62","title":"swc"},"63":{"body":"Rome 可以用来对JavaScript、TypeScript、HTML、JSON、Markdown 和 CSS 进行lint、编译、打包等功能,它的目标是替代Babel、ESLint、webpack、Prettier、Jest等。 一开始Rome是使用Typescript开发,目前正在用Rust进行重写。有趣的是: Rome的作者也是Babel的作者, 后者还是他在学习编译原理时做的。","breadcrumbs":"使用 Rust 增强 JS » Rome","id":"63","title":"Rome"},"64":{"body":"fnm 是一个简单易用、高性能的Node版本管理工具,还支持.nvmrc文件(nvm的node版本描述文件)","breadcrumbs":"使用 Rust 增强 JS » fnm","id":"64","title":"fnm"},"65":{"body":"boa 是一个高性能的javascript词法分析器,解析器和解释器,目前还是实验性质的。","breadcrumbs":"使用 Rust 增强 JS » boa","id":"65","title":"boa"},"66":{"body":"napi 可以用于构建基于Node API的Nodejs插件,目前由nextjs主导开发。","breadcrumbs":"使用 Rust 增强 JS » napi","id":"66","title":"napi"},"67":{"body":"volt 是一个现代化的、高性能、安全可靠的Javascript包管理工具。目前该库正处于活跃开发阶段,只供学习使用。","breadcrumbs":"使用 Rust 增强 JS » volt","id":"67","title":"volt"},"68":{"body":"neon 可以用于写安全、高性能的原生Nodejs模块。 fn make_an_array(mut cx: FunctionContext) -> JsResult { // 创建一些值: let n = cx.number(9000); let s = cx.string(\"hello\"); let b = cx.boolean(true); // 创建一个新数组: let array: Handle = cx.empty_array(); // 将值推入数组中 array.set(&mut cx, 0, n)?; array.set(&mut cx, 1, s)?; array.set(&mut cx, 2, b)?; // 返回数组 Ok(array)\n} register_module!(mut cx, { cx.export_function(\"makeAnArray\", make_an_array)\n})","breadcrumbs":"使用 Rust 增强 JS » neon","id":"68","title":"neon"},"69":{"body":"resvg-js 是一个高性能svg渲染库,使用Rust + Typescript实现。下面的图片通过svg实现(羞~~~):","breadcrumbs":"使用 Rust 增强 JS » resvg-js","id":"69","title":"resvg-js"},"7":{"body":"性能对比 metrics 该库对Rust现存的数据库连接服务进行性能测试,若大家有性能上的需求,值得一看 通用 launchbadge/sqlx 异步实现、高性能、纯Rust代码的SQL库,支持PostgreSQL, MySQL, SQLite,和 MSSQL. ORM rbatis/rbatis 国内团队开发的ORM,异步、性能高、简单易上手 diesel-rs/diesel 安全、扩展性强的Rust ORM库,支持Mysql、Postgre、SqlLite Mysql blackbeam/rust-mysql-simple 纯Rust实现的Mysql驱动,提供连接池 blackbeam/mysql_async 基于Tokio实现的异步Mysql驱动 上面两个都是一个团队出品,前者文档更全、star更多,建议使用前者 Postgre sfackler/rust-postgres 纯Rust实现的Postgre客户端 Sqlite rusqlite 用于 Sqlite3 的Rust客户端","breadcrumbs":"日常开发常用库 » SQL客户端","id":"7","title":"SQL客户端"},"70":{"body":"deno_lint , 由deno团队出品的lint工具,支持Javascript/Typescript,支持Deno也支持Node。 优点之一就是极致的快: [ { \"name\": \"deno_lint\", \"totalMs\": 105.3750100000002, \"runsCount\": 5, \"measuredRunsAvgMs\": 21.07500200000004, \"measuredRunsMs\": [ 24.79783199999997, 19.563640000000078, 20.759051999999883, ] }, { \"name\": \"eslint\", \"totalMs\": 11845.073306000002, \"runsCount\": 5, \"measuredRunsAvgMs\": 2369.0146612000003, \"measuredRunsMs\": [ 2686.1039550000005, 2281.501061, 2298.6185210000003, ] }\n]","breadcrumbs":"使用 Rust 增强 JS » deno_lint","id":"70","title":"deno_lint"},"71":{"body":"rslint 是一个高性能、可定制性强、简单易用的Javascript/Typescript lint分析工具。 $ echo \"let a = foo.hasOwnProperty('bar');\" > foo.js\n$ rslint ./foo.js\nerror[no-prototype-builtins]: do not access the object property `hasOwnProperty` directly from `foo` ┌─ ./foo.js:1:9 │\n1 │ let a = foo.hasOwnProperty('bar'); │ ^^^^^^^^^^^^^^^^^^^^^^^^^ │\nhelp: get the function from the prototype of `Object` and call it │\n1 │ let a = Object.prototype.hasOwnProperty.call(foo, 'bar'); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ │ ╧ note: the method may be shadowed and cause random bugs and denial of service vulnerabilities Outcome: 1 fail, 0 warn, 0 success help: for more information about the errors try the explain command: `rslint explain `","breadcrumbs":"使用 Rust 增强 JS » rslint","id":"71","title":"rslint"},"72":{"body":"rusty_v8 是v8的Rust语言绑定,底层封装了c++ API。","breadcrumbs":"使用 Rust 增强 JS » rusty_v8","id":"72","title":"rusty_v8"},"73":{"body":"","breadcrumbs":"使用 Rust 增强 JS » 用WASM增强JS","id":"73","title":"用WASM增强JS"},"74":{"body":"wasm(web assembly) 是一种低级语言,它运行在浏览器中,可以和javascript相互调用,几乎所有浏览器都支持, 而且目前有多种高级语言都可以直接编译成wasm,更是大大增强了它的地位。 目前来说Rust可以编译成wasm,虽然还不够完美,但是它正在以肉眼可见的速度快速发展中。因此同时使用Rust和Javascript成为了一种可能:将Rust编译成wasm,再跟js进行交互,两者共生共存,各自解决擅长的场景(wasm性能高,js开发速度快)。","breadcrumbs":"使用 Rust 增强 JS » wasm","id":"74","title":"wasm"},"75":{"body":"yew 是一个正在活跃开发的Rust/Wasm框架,用于构建Web客户端应用。","breadcrumbs":"使用 Rust 增强 JS » yew","id":"75","title":"yew"},"76":{"body":"[gloo]是一个模块化的工具,使用Rust/WASM构建快速、可靠的Web应用。 use gloo::{events::EventListener, timers::callback::Timeout};\nuse wasm_bindgen::prelude::*; pub struct DelayedHelloButton { button: web_sys::Element, on_click: events::EventListener,\n} impl DelayedHelloButton { pub fn new(document: &web_sys::Document) -> Result { // 创建 `