关于rust:Rust-Mac-安装报错

报错curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs --no-modify-path | sh# outputerror: could not amend shell profile: '/Users/xxxx/.bashrc': could not write rcfile file: '/Users/xxxx/.bashrc': Permission denied (os error 13)解决办法:curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | bash -s -- -y --no-modify-path

August 2, 2021 · 1 min · jiezi

关于rust:Vercel-中的-Rust-和-WebAssembly-serverless-函数

作者:Michael Yuan, WasmEdge MaintainerVercel 是开发和托管 Jamstack 应用程序的当先平台。与传统 Web 应用程序在 runtime 从服务器动静生成 UI 不同,Jamstack 应用程序由动态 UI( HTML 和 JavaScript )和一组通过 JavaScript 反对动静 UI 元素的 serverless 函数组成。 Jamstack 的形式有很多益处。 这其中最重要的益处之一是其弱小的性能。因为 UI 不再从核心服务器的 runtime 生成,因而服务器上的负载要少得多,咱们能够通过边缘网络(例如 CDN)部署 UI。 然而,边缘 CDN 只解决了散发动态 UI 文件的问题。后端的 serverless 函数可能依然很慢。事实上,目前风行的 serverless 平台存在家喻户晓的性能问题,例如冷启动迟缓,对于交互式应用程序尤其如此。在这方面, WebAssembly 大有可为。 应用 WasmEdge,一个 CNCF 托管的云原生的 WebAssembly runtime , 开发者能够编写高性能 serverless 函数,部署在公共云或边缘计算节点上。本文中,咱们将摸索如何应用 Rust 编写的 WasmEdge 函数来驱动 Vercel 应用程序后端。 为什么在 Vercel Serverless 应用 WebAssembly ?Vercel 平台曾经有了十分易于应用的 serverless框架 ,能够部署 Vercel 中托管的函数。正如下面探讨的,应用 WebAssembly 和 WasmEdge 是为了进一步提高性能。用 C/C++、Rust 和 Swift 写的高性能函数能够轻松编译成 WebAssembly。这些 WebAssembly 函数比 serverless 函数中罕用的 JavaScript 或 Python 快得多。 ...

July 29, 2021 · 4 min · jiezi

关于rust:Rust定时组件asyncrs-futurestimer实现原理

我的项目中须要应用定时器,看了Rust官网提供的 futures-timer的源码 ,将实现原理这里记录下来。 以async-rs / futures-timer v3.0.0 源码为根底。 很多同学是java开发,因而与java的Timer比照,不便理解(待补充)。 另外本文不蕴含任何源码,这样不须要懂得Rust语言也能了解其中的设计原理。想看源码的同学,置信浏览本文当前再去看源码应该高深莫测。 组成构造rust数据结构蕴含Delay、Heap、ArcList、Timer。其中,Delay是用户定义的一个提早工作,但仅蕴含延迟时间,没有设置工作来回调。Heap是一个最小堆,每个节点就是Delay,依据Delay的工夫作为优先级,每次获取工夫最近的Delay。ArcList是一个队列,其中的对象也是Delay。 Timer是总体的定时器,封装了Heap和ArcList。 管制组件包含waker(待补充) 实现原理插入工作:用户创立Delay当前,写入ArcList后,由timer将Delay迁徙到Heap中,最终Delay从Heap堆顶取出,实现整个生命周期。另外,ArcList反对并发写入,Heap不反对并发,这里ArcList起到一个音讯队列的作用。 注册回调:Delay工作插入Timer,也就是插入ArcList当前,同时也将Delay注册到监听器waker中,Waker是Timer中的一个监听器。当Delay工作在Heap中超时时,也就是从堆顶取出时,Waker触发Delay工作。这里的触发其实是一个future返回后果,Delay实现了future接口,当Delay注册到Timer当前,用户期待Delay的future实现,等Delay被触发时,future返回后果,以此达到定时成果。 回调告诉Timer中有一个监听线程解决Heap堆里的Delay的超时,流程是取出堆顶的Delay当前,计算下一个Delay的工夫,而后sleep。另外,当有新Delay插入时,也会唤醒这个线程,从新判断是否有Delay曾经超时,而后再次sleep。 其余性能实现原理工夫重置当批改之前曾经存在的Delay的执行工夫时,之前写入的Delay应该有效。采纳的办法是,在Delay中减少两个计数字段,一个是当初重置过几次t,一个是工作写入Heap时t的值。当重置Delay时,将新生成一个Delay,两个计数字段都是t+1,插入ArcList,并且会批改Heap中原来的Delay,第一个计数字段变成t+1,而第二个字段还是t。当原来的Delay超时时,两个字段不统一,主动疏忽。 注意事项官网提供async-rs / futures-timer组件。须要留神的是,async-rs / futures-timer在版本0.0.6当前将Interval性能删除了,Interval性能指的是固定周期定时工作性能,只保留了Delay性能,Delay性能指的是执行一次的工作。尽管Interval性能是对Delay做了一层封装Delay性能,然而用户手动实现还是有肯定复杂度。 tokio也提供tokio::time组件,反对Delay性能、Interval性能、以及TimeOut性能,TimeOut性能指的是如果一个Future在指定工夫内没有返回后果就超时。须要留神的是tokio::time只在["time"]feature上反对。 参考https://github.com/async-rs/f...https://zhuanlan.zhihu.com/p/...https://wiki.jikexueyuan.com/...https://docs.rs/tokio/1.6.1/t...

July 24, 2021 · 1 min · jiezi

关于rust:Rust

Rust入门Rust官网 装置以下内容皆基于MACWindows及其他装置 rust装置器和包管理工具curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh命令执行失败走 装置镜像export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/r...export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/r...curl https://sh.rustup.rs -sSf | sh后果如下~ ➤ curl https://sh.rustup.rs -sSf | shinfo: downloading installerWelcome to Rust!This will download and install the official compiler for the Rustprogramming language, and its package manager, Cargo.Rustup metadata and toolchains will be installed into the Rustuphome directory, located at:...You can uninstall at any time with rustup self uninstall andthese changes will be reverted.Current installation options: default host triple: x86_64-apple-darwin default toolchain: stable (default) profile: default modify PATH variable: yes1) Proceed with installation (default)2) Customize installation3) Cancel installation抉择输出 1 为稳固版本呈现以下代码就实现了 ...

July 23, 2021 · 2 min · jiezi

关于rust:Compound-Data-Type-of-Rust

概 述良久没有更新rust相干的内容了,更新一波Rust的内容,本篇讲介绍一下Rust中的复合数据类型。Composite Type复合数据类型是一种数据类型,它能够原始的根本数据类型和其它的复合类型所形成, 形成一个复合类型的动作,又称作组合。 本文讲介绍一下在Rust中有tuple、array、struct、enum几个复合类型。 tupletuple即元组,元组类型是由多个不同类型的元素组成的复合类型,通过()小括号把元素组织在一起成一个新的数据类型。元组的长度在定义的时候就曾经是固定的了,不能批改,如果指定了元素的数据类型,那么你的元素就要对号入座!!!否则编译器会教训你! 例子: fn main() { // 指定数据类型 let tup_type:(i8,i32,bool) = (21,-1024,true); // 解构元素 let (one,two,three) = tup_type; // 二维的元组 let tup_2d:(f64,(i8,i32,bool)) = (3.1415927,(one,two,three)); println!("tup_2d = {:?}",tup_2d); // 索引 println!(" = {:?}",tup_2d.0);}元组的拜访形式有好几种,通过下标去拜访,也能够应用解构赋值给新的变量去拜访,然而不反对迭代器去拜访。 for v in tup_2d.1.iter() { println!("{}",v)} Compiling playground v0.0.1 (/playground)error[E0599]: no method named `iter` found for tuple `(i8, i32, bool)` in the current scope --> src/main.rs:10:23 |10 | for v in tup_type.iter() { | ^^^^ method not found in `(i8, i32, bool)`error: aborting due to previous errorFor more information about this error, try `rustc --explain E0599`.error: could not compile `playground`To learn more, run the command again with --verbose.元组的每个元素的类型能够不同,因而您无奈对其进行迭代。元组甚至不能保障以与类型定义雷同的顺序存储数据,因而即便您本人为它们实现Iterator,它们也不适宜进行无效的迭代。 ...

July 20, 2021 · 2 min · jiezi

关于rust:用-WasmEdge-和-YoMo-对实时数据流进行-AI-推理

YoMo 是一个用于辅助开发者不便构建分布式云零碎(Geo-Distributed Cloud System)的编程框架。YoMo 的通信层构建在 QUIC 协定之上,带来高速数据传输的同时,内置了 Streaming Serverless 的“流函数”,大幅晋升了分布式云零碎的开发体验。YoMo 构建的分布式云零碎在近场算力和终端之间提供了超高速通信机制,在 Metaverse、VR/AR、IoT 等畛域有宽泛的利用场景。 YoMo 应用 Go 语言编写,Streaming Serverless 局部应用了 Golang 的插件和共享库动静加载用户代码和共享库,但也给开发者带来了一些局限性,尤其是应用 Windows 的开发者。加之Serverless 架构对隔离的刚性需要,这使得 WebAssembly 成为运行用户定义函数的绝佳抉择。例如在 AR/VR、智能工厂里做实时 AI 推理的过程中,摄像头能够通过 YoMo 将实时的非结构化数据发送到近场 MEC (多拜访边缘计算)设施中的计算节点,并主动执行托管的 AI 推理函数。当 AI 推理实现,YoMo将 AI 计算结果实时发送给端设施。 然而,YoMo 面临的挑战是在边缘计算节点中合并和治理由多个内部开发者编写的处理程序函数。这须要在不就义性能的状况下对这些函数进行 runtime 隔离。传统的软件容器解决方案,如 Docker,无奈胜任这项工作,因为太重且速度太慢,无奈解决实时工作。 WebAssembly 提供了一个轻量级高性能的软件容器。它非常适合作为 YoMo 数据处理 handler 函数的 runtime。 本文中,咱们将向你展现如何为基于 Tensorflow 的图片辨认创立 Rust 函数,将其编译为 WebAssembly,而后应用 YoMo 将其作为流数据 handler 运行。咱们应用 WasmEdge 作为 WebAssembly runtime,因为与其它 WebAssembly runtime 相比,WasmEdge 提供了最佳性能和最高灵便度。WasmEdge 是惟一稳固反对 Tensorflow 的 WebAssembly 虚拟机。 YoMo 通过 WasmEdge 的 Golang API治理 WasmEdge VM 实例和容器内的 WebAssembly 字节码利用。 ...

July 13, 2021 · 4 min · jiezi

关于rust:Rust基本数据类型

本文介绍Rust提供的内置数据类型。 布尔类型布尔类型代表“是”和“否”的逻辑值。它有两个值:true和false,个别用在逻辑表达式中,能够执行“与”、“或”、“非”等运算: fn main() { let x = true; let y: bool = !x; //false, 取反运算 let z = x && y; //false,逻辑与运算,带有短路性能 let z = x || y; //true,逻辑或运算,带有短路性能 let z = x & y; //false,按位与运算,不带短路性能 let z = x | y; //true,按位或运算,不带短路性能 let z = x ^ y; //true,按位异或运算,不带短路性能}一些比拟运算表达式的类型是布尔类型: fn logical_op(x: i32, y: i32) { let z = x < y; // z是布尔类型 println!("z = {}", z);}布尔类型表达式能够用在if/while表达式中,作为条件表达式: ...

May 26, 2021 · 2 min · jiezi

关于rust:论好文章和烂文章

简介:咱们为何写作?对于许多技术同学来说,写作是一件比写代码艰难许多的事件,和电脑相顾无言数小时,发现自己写不出什么像样的货色来,着实不是一种很好的体验。 作者 | 许晓斌 起源 | 阿里巴巴云原生公众号 写作动机咱们为何写作?对于许多技术同学来说,写作是一件比写代码艰难许多的事件,和电脑相顾无言数小时,发现自己写不出什么像样的货色来,着实不是一种很好的体验。即使对于有些教训的人来说,写四千字品质尚可的文章,我预计也要花 6 小时以上的工夫,这还不算平时素材积攒的工夫耗费。 这么麻烦费劲的事件,为什么要去做呢?我认为此事有着极大的价值,这个价值分两层,我暂且称之为表层价值和深层价值。 表层价值是极其功利的,例如有同学想降职,而降职的一项指标是集体影响力,那么写文章就能晋升集体影响力;例如有团队 Team Leader 想招聘,那怎么让他人理解你及你的团队呢,写文章也是个不错的办法;再有一些就是冲着下级或者利益相干方写的文章,以相似我的项目汇报的形式写的文章。表层价值的外围关注点其实并不在文章自身,而在于文章背地的人,作者对读者的冀望往往不在于读者认可文章内容,也不冀望读者参加对内容的探讨,而仅仅是冀望读者疾速地认可作者这个人。 只关注表层价值去写文章,十分的轻重倒置。就好比写一篇探讨 PM 2.5 的科普文章,如果你一上来就冲着采购本人的空气净化器这个动机去写,其恶臭很快会从字里行间流露出来。 与表层价值绝对的,我认为任何一篇文章都应该从深层价值登程。这个所谓深层价值就是文章的内容,文章的观点,文章须要尽可能地主观,要向学术真谛的态度迫近。写一篇文章是因为对一个问题有着本人的思考,并且去具体理解了很多人的思考,发现了一些观点的抵触,并且不会为了政治正确去投合他人的观点;尽我所能把那些我认为有价值的想法,总结下来,用清晰、乏味的办法流传给别人;我能领会到写作的激情,这种激情来自于思维的乐趣,来自于观点的碰撞。这个写作的过程,本人的思维有成长,通过大量的逻辑思考,我的思维失去晋升;其次写进去的文章,对读者有着很高的价值,因为有价值的常识失去了流传。 还有一种写作动机就是想要流传有价值的技术,例如 Pivatal 公司的布道师 Josh Long 就写了大量的技术介绍文章,也有很多精彩的演讲。我曾问他为什么可能做的如此杰出,以致于多年被评为寰球 Java 畛域最有影响力的 20 人之一。他的答复是这样的: I think that people don’t trust technology, they trust people. So, while it is possible that the spring team could just publish good documentation and leave it for the world to find, it’s far more compelling when u feel u can ask questions of someone. And u can see that they’re having fun.I love Spring because it has made millions of lives easier. It makes me happy to think about its application, to see people happy with its possibilities.一篇文章写进去,是因为作者青睐一项技术,从心田认可技术的价值,置信技术的后劲;还是因为作者想抛售什么货色。这两者动机的差别,略微仔细的读者很快就能发现。当然,上述动机常常混合在一起,然而如果写作的次要动机都在表层,那么基本上这样的文章也就没什么价值了。 ...

May 17, 2021 · 2 min · jiezi

关于rust:使用MSVC工具链以及VSCode搭建Rust开发环境

本文介绍Rust在Windows下开发环境的装置以及配置。 1 装置实用于MSVC ABI的工具链在VS2019 C/C++工具链官方网站出下载生成工具,并勾选Visual Studio 生成工具进行下载安装即可 留神:这个工具没有装置Visual Studio,仅仅装置了MSVC的工具链。2 配置Rust工具链装置地位增加以下两个环境变量,用于指定cargo和rustup的装置地位: CARGO_HOME:指定cargo的装置目录。RUSTUP_HOME:指定rustup的装置目录。留神:它们是可选项,如果不指定,默认别离装置到家目录下的.cargo和.rustup目录下。 3 配置rustup国内源增加以下两个环境变量,别离改用中科大源来更新工具链和rustup本身: RUSTUP_DIST_SERVER:https://mirrors.ustc.edu.cn/rust-staticRUSTUP_UPDATE_ROOT:https://mirrors.ustc.edu.cn/rust-static/rustup4 配置cargo中科大源在cargo装置目录下创立一个文件,名为config,其中的内容指定为: [source.crates-io]registry = "https://github.com/rust-lang/crates.io-index"replace-with = 'ustc'[source.ustc]registry = "git://mirrors.ustc.edu.cn/crates.io-index"5 装置RLS和std源码RLS的全程是:Rust Language Server,是一个守护过程,用于和开发时应用的IDE进行IPC通信,实现代码提醒、跳转到定义、显示变量类型等性能。 std是Rust的规范库。默认状况下,Rust装置的是规范库的二进制码,而增加源码后容易不便咱们查问局部规范库的实现。 应用以下命令装置: rustup component add rls --toolchain stable # RLSrustup component add rust-analysis --toolchain stable # 剖析工具rustup component add rust-src --toolchain stable # std源码6 装置VSCode插件装置插件rust-analyzer以及其依赖项rust-analyzer server。 默认状况下,rust-analyzer server由VSCode主动从Github上拉取。如果因为网络起因装置失败,能够执行如下操作来手动从源码编译装置: 在本机装置node.js环境,用于打包生成VSCode插件。从这里下载rust-analyzer server源代码。解压缩,在源码根目录下执行以下命令: cargo xtask install至此Rust环境装置结束。

May 11, 2021 · 1 min · jiezi

关于rust:libp2prs-v030-版本介绍

v0.3.0于4.23公布,应用 AsyncRead & AsyncWrite来替换咱们的 ReadEx & WriteEx & SplitEx;简化了 Kad/DHT 的实现逻辑。 批改ReadEx & WriteEx & SplitEx:最后咱们尝试借助 async-trait 来定义本人 io 操作相干的 Trait,以便更纯正的应用 async/await 的形式来编写代码。 以 ReadEx 为例大略是上面这样: #[async_trait]pub trait ReadEx { async fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error>;}应用咱们定义的 Trait 的确给咱们带来了一些益处,无需再编写状态机式的代码。雷同的逻辑能够用更容易了解的形式实现;同时也引入了一些问题: 很难进行读写拆散咱们先来看看为什么须要读写拆散,应用 async/await 形式相较于 poll 的形式,失去了一些控制能力,无奈在一个 Future 中同时做多件事,比方咱们这里的读写:let mut socket = ...;// poll 形式fn poll(socket: Pin<&mut Socket>, cx: Context) { match socket.poll_read(cx) { Poll::Pending => { // 这里就有机会在 poll 中同时解决读写 socket.poll_write(cx) } Poll::Ready => {...} }}// async/awaitasync fn handle_socket(socket: &mut Socket) { socket.read().await; // 这里就没方法在在 read 还没筹备好的时候,持续去执行write操作 // 因为 read & write 都是须要 &mut T的所以也没方法借助select来打到目标 // let read_fut = socket.read(); // let write_fut = socket.write(); // select(read_fut, write_fut).await;}鉴于这样的起因,咱们就须要将读写离开来解决。同时读写放在不同的协程中解决,代码逻辑也会更清晰。 ...

April 26, 2021 · 1 min · jiezi

关于rust:rust中的箭头函数-和的区别

初学rust,置信你会像我一样被rust庞杂的语法震惊到 上面的代码看起来像是java和JavaScript的结合体 use std::io::prelude::*;use std::fs::OpenOptions;fn main() -> std::io::Result<()> { let mut file = OpenOptions::new() .append(true).open("D:\\text.txt")?; file.write(b" APPEND WORD")?; Ok(())}fn main() { let mut v = vec![1, 2, 4, 8]; println!("{}", match v.get(0) { Some(value) => value.to_string(), None => "None".to_string() });}对于 => 的用法rust作为一门只有6岁的新语言,从js、java、python等高级语言中排汇了很多语法 => 在JavaScript中示意箭头函数,是一种为了为了在定义函数时省略function关键字的语法糖 参考链接:TypeScript 函数JavaScript初学者必看“箭头函数” 对于 -> 的用法-> 表明的是

March 22, 2021 · 1 min · jiezi

关于rust:rustanalyzer加载时间过长

开发环境:vscode+rust-analyzer问题:vscode始终卡在fetching metadata阶段。办法:运行cargo metadata,发现 Blocking waiting for file lock on package cache运行 rm -rf ~/.cargo/.package-cache删除cargo的缓存,而后再运行cargo metadata后发现没有blocking的提醒,重启vscdoe,加载rust-analyzer胜利。 参考 rust-analyzer issue616rust cargo build始终呈现 Blocking waiting for file lock on package cache

March 9, 2021 · 1 min · jiezi

关于rust:华为微软和AWS等公司组建新Rust-基金会

SegmentFault 思否音讯,当地工夫2月8日,Rust基金会(Rust Foundation)正式官宣成立。 Rust 是一款风行的零碎编程语言,最后是Mozilla外部的一个副我的项目,专门用于火狐浏览器的开发工作,号称是C语言和C++的继任者。在过来的十年里,Rust成为最受开发人员喜爱的语言之一。 近几年Rust逐步开始在游戏开发、嵌入式设施、Web编程、网络、开发者工具以及其他软件畛域施展重要作用。依据微软GitHub 上的数据,Rust采用率在2019年增长了 235%。并且它还有很多引人瞩目的概念,比方将对性能的低级(low-level)管制与古代语言性能联合在一起、“没有数据争用的并发性”、“没有垃圾回收的内存安全性” 和 “无惧骇客” 等。 新Rust董事会将由5名来自开创公司 AWS、华为、谷歌、微软和 Mozilla 的董事和 5 名来自我的项目领导层的董事(其中2名代表外围团队,3名来自3个我的项目畛域:可靠性、品质、和合作)组成。作为Rust 我的项目的发起者,Mozilla 现已将包含商标等在内的所有基础设施资产,转移到了新成立的 Rust 基金会。 其成立的起因是在破费了大量工夫钻研潜在的单干组织之后,外围团队认为成立一个独立的实体是最好的抉择。Rust 是一个以价值为导向的技术和社区,外围团队始终没有找到一个与他们的社区指标相符合的单干组织。尽管成立一个独立的基金会意味着更多的工作,然而权衡利弊之后他们认为这样做是值得的。 Rust基金会的主旨是致力于Rust编程语言及其生态治理、聚焦于反对负责管理和倒退Rust我的项目的维护者群,并为Rust贡献者提供培训、软件和组织撑持。Rust 外围团队认为,保护人员欢快地进行工作可能成就最好的Rust,而让基金会聚焦于保护人员则能为所有人成就更好的 Rust。 基金会可能提供反对(将来董事会将要决定是否批准的几个例子): 团队须要法律倡议时分割律师,例如如何为 crates.io 团队解决 DMCA,或解决编译器团队的许可问题领取某个团队所需的工具和服务费用,例如为须要进行同步会议的团队购买视频通话平台,或 help@crates.io 须要的服务台工具反对倒退Rust的领导力,反对领导力多样化的相干致力,例如 Increasing Rust's Reach 我的项目进行年度考察,收集和总结更广范畴内的社区对我的项目的诉求集体相干问题Q: 基金会仅会在美国招聘雇员吗? A: 基金会将在寰球范畴内招聘,不仅限于美国。作为抉择地点的一部分,咱们优先钻研了如何无效治理这件事。罕用的办法之一是应用一个寰球业余待业组织(PEO),即专门从事寰球员工和待业法律的治理的代理公司。当然,最终基金会负责决定这些操作细节。 Q:集体是否能够向 Rust 基金会进行钱款捐献? A:当初不行,未来兴许能够。首先,咱们明确地优先思考从公司取得捐献而不是集体捐献。咱们置信,集体,尤其是那些正为 Rust 我的项目贡献力量的人,曾经为我的项目付出了很多。只管 Rust 的基础设施曾经有不少公司资助,然而在此之前,对于 Rust 我的项目的更宽泛需要,咱们未曾有条件提供如此间接的资助。咱们心愿公司承当起责任,回馈 Rust 社区、回馈我的项目志愿者所奉献的价值;资助 Rust 基金会是这样做的一种重要形式。对于招聘贡献者的问题还探讨了咱们激励企业赞助者对 Rust 我的项目进行投资的一些其余形式。 Q:集体是否能够成为基金会成员? A:咱们曾经探讨过扩充成员数量以涵盖 Rust 团队成员和维护者,然而目前尚无打算扩大成员资格去接收集体 Rust 用户。只管这仿佛是一件很不错的事件,然而想要做得正确很难,因而咱们抉择让基金会决定是否进行、何时进行。 Q:集体是否能够成为基金会志愿者? A:咱们应该使人可能容易地理解到如何帮忙。咱们认为,做到这一点的最佳办法是防止创立“基金会志愿者”与“我的项目志愿者”的概念,仅保留“Rust 志愿者”这一概念。举一个可能会帮忙了解的例子。在后面的问题中,咱们提到了制订人类可读版章程并将其翻译成多种语言的打算。咱们没有为基金会征召本人的翻译人员,而是分割了 Rust 社区团队,他们批准组织这项工作并提供人力。 Rust基金会官网链接:https://foundation.rust-lang.org/ Rust基金会FAQ(中文版)链接:https://github.com/happyaron/foundation-faq-2020/blob/l10n/zh_CN/FAQ-zh_CN.md#q-sponsors Rust基金会官宣链接:https://foundation.rust-lang.org/posts/2021-02-08-hello-world/ ...

February 10, 2021 · 1 min · jiezi

关于rust:华为微软和AWS等公司组建新Rust-基金会

SegmentFault 思否音讯,当地工夫2月8日,Rust基金会(Rust Foundation)正式官宣成立。 Rust 是一款风行的零碎编程语言,最后是Mozilla外部的一个副我的项目,专门用于火狐浏览器的开发工作,号称是C语言和C++的继任者。在过来的十年里,Rust成为最受开发人员喜爱的语言之一。 近几年Rust逐步开始在游戏开发、嵌入式设施、Web编程、网络、开发者工具以及其他软件畛域施展重要作用。依据微软GitHub 上的数据,Rust采用率在2019年增长了 235%。并且它还有很多引人瞩目的概念,比方将对性能的低级(low-level)管制与古代语言性能联合在一起、“没有数据争用的并发性”、“没有垃圾回收的内存安全性” 和 “无惧骇客” 等。 新Rust董事会将由5名来自开创公司 AWS、华为、谷歌、微软和 Mozilla 的董事和 5 名来自我的项目领导层的董事(其中2名代表外围团队,3名来自3个我的项目畛域:可靠性、品质、和合作)组成。作为Rust 我的项目的发起者,Mozilla 现已将包含商标等在内的所有基础设施资产,转移到了新成立的 Rust 基金会。 其成立的起因是在破费了大量工夫钻研潜在的单干组织之后,外围团队认为成立一个独立的实体是最好的抉择。Rust 是一个以价值为导向的技术和社区,外围团队始终没有找到一个与他们的社区指标相符合的单干组织。尽管成立一个独立的基金会意味着更多的工作,然而权衡利弊之后他们认为这样做是值得的。 Rust基金会的主旨是致力于Rust编程语言及其生态治理、聚焦于反对负责管理和倒退Rust我的项目的维护者群,并为Rust贡献者提供培训、软件和组织撑持。Rust 外围团队认为,保护人员欢快地进行工作可能成就最好的Rust,而让基金会聚焦于保护人员则能为所有人成就更好的 Rust。 基金会可能提供反对(将来董事会将要决定是否批准的几个例子): 团队须要法律倡议时分割律师,例如如何为 crates.io 团队解决 DMCA,或解决编译器团队的许可问题领取某个团队所需的工具和服务费用,例如为须要进行同步会议的团队购买视频通话平台,或 help@crates.io 须要的服务台工具反对倒退Rust的领导力,反对领导力多样化的相干致力,例如 Increasing Rust's Reach 我的项目进行年度考察,收集和总结更广范畴内的社区对我的项目的诉求集体相干问题Q: 基金会仅会在美国招聘雇员吗? A: 基金会将在寰球范畴内招聘,不仅限于美国。作为抉择地点的一部分,咱们优先钻研了如何无效治理这件事。罕用的办法之一是应用一个寰球业余待业组织(PEO),即专门从事寰球员工和待业法律的治理的代理公司。当然,最终基金会负责决定这些操作细节。 Q:集体是否能够向 Rust 基金会进行钱款捐献? A:当初不行,未来兴许能够。首先,咱们明确地优先思考从公司取得捐献而不是集体捐献。咱们置信,集体,尤其是那些正为 Rust 我的项目贡献力量的人,曾经为我的项目付出了很多。只管 Rust 的基础设施曾经有不少公司资助,然而在此之前,对于 Rust 我的项目的更宽泛需要,咱们未曾有条件提供如此间接的资助。咱们心愿公司承当起责任,回馈 Rust 社区、回馈我的项目志愿者所奉献的价值;资助 Rust 基金会是这样做的一种重要形式。对于招聘贡献者的问题还探讨了咱们激励企业赞助者对 Rust 我的项目进行投资的一些其余形式。 Q:集体是否能够成为基金会成员? A:咱们曾经探讨过扩充成员数量以涵盖 Rust 团队成员和维护者,然而目前尚无打算扩大成员资格去接收集体 Rust 用户。只管这仿佛是一件很不错的事件,然而想要做得正确很难,因而咱们抉择让基金会决定是否进行、何时进行。 Q:集体是否能够成为基金会志愿者? A:咱们应该使人可能容易地理解到如何帮忙。咱们认为,做到这一点的最佳办法是防止创立“基金会志愿者”与“我的项目志愿者”的概念,仅保留“Rust 志愿者”这一概念。举一个可能会帮忙了解的例子。在后面的问题中,咱们提到了制订人类可读版章程并将其翻译成多种语言的打算。咱们没有为基金会征召本人的翻译人员,而是分割了 Rust 社区团队,他们批准组织这项工作并提供人力。 Rust基金会官网链接:https://foundation.rust-lang.org/ Rust基金会FAQ(中文版)链接:https://github.com/happyaron/foundation-faq-2020/blob/l10n/zh_CN/FAQ-zh_CN.md#q-sponsors Rust基金会官宣链接:https://foundation.rust-lang.org/posts/2021-02-08-hello-world/ ...

February 10, 2021 · 1 min · jiezi

关于rust:WebAssembly-Landscape-2021

这张图总结了 WebAssembly 倒退到当初的周边工具链、扩大以及在浏览器端、服务器端的一些利用。当然目前 WebAssembly 生态还不是很欠缺,比方没有平安、测试方面的工具。 当然,这张图应该会有一些漏掉的好我的项目,欢送大家通过 github 补充或间接与我交换。如果有感觉不适合的我的项目分类,也能够与我交换呀。 当有新的我的项目补充后,我会再从新制作一张图收回来,心愿这张图越来越长,Wasm 生态越来越欠缺。 最初感激 juntao 与 iyacontrol 在制作这张图过程中的帮忙! 文字版在这里:https://github.com/second-sta...

February 4, 2021 · 1 min · jiezi

关于rust:libp2prs-infoserver-实现

模块地址:https://github.com/netwarps/l... 在上一篇文章的开端有提到,会采纳web server的形式提供相干的restful api,能够在内部观测网络收发包的状况。目前已设计实现,在这里简略分享一下设计过程。 实现构想设计Metric时,为了缩小与swarm通信的次数,咱们在control中放了一份metric的clone。对于api server来说,咱们齐全能够借助control提供的metric相干操作方法,取得咱们想要失去的网络流量数据,以及以后连贯的一些相干状况。 框架介绍Tide作为rust的一个web利用框架,实现了一系列相干的路由性能,能够很不便地构建API;同时,serde的序列化/反序列化性能,能帮忙咱们将数据格式化成json类型,更容易浏览和解析。 路由注册以get办法为例,在Tide中,通过以下这种形式实现路由注册: server.at(path).get(method)at办法和get办法如下所示: // self为server对象 pub fn at<'a>(&'a mut self, path: &str) -> Route<'a, State> { let router = Arc::get_mut(&mut self.router) .expect("Registering routes is not possible after the Server has started"); Route::new(router, path.to_owned()) } // self为Route对象 pub fn get(&mut self, ep: impl Endpoint<State>) -> &mut Self { self.method(http_types::Method::Get, ep); self }能够看到,method参数实际上是一个impl Trait。在这个实现了这个trait的类型中,有这样一种模式: #[async_trait]impl<State, F, Fut, Res> Endpoint<State> for Fwhere State: Clone + Send + Sync + 'static, F: Send + Sync + 'static + Fn(Request<State>) -> Fut, Fut: Future<Output = Result<Res>> + Send + 'static, Res: Into<Response> + 'static,{ async fn call(&self, req: Request<State>) -> crate::Result { let fut = (self)(req); let res = fut.await?; Ok(res.into()) }}对应到咱们的代码中,泛型State为Control,Fn咱们能够实现为一个async的办法,传入参数是Request,返回值类型为tide::Result。 ...

January 6, 2021 · 2 min · jiezi

关于rust:Rust布道者张汉东倾授入门Rust初学者都要攻破哪些难点

摘要:Rust语言学习曲线过于平缓?初学者看懂这张思维导图,疾速入门。Rust语言这两年的热度大家引人注目,作为一个有着突破性改革意义的语言,其光鲜背地也有诸多质疑,对于想要在零碎编程语言上更上一层楼的开发者来说,学习Rust语言既是一次新的尝试,也是一次编程思维上的挑战。 最近,技术畅销书《Rust编程之道》的作者张汉东总结了一份“Rust初学者学习门路”思维导图,作为Rust语言的头等布道者,张汉东参加了国内Rust社区的治理和经营,长期在各大常识平台输入高质量的学习教程,能够说是入门Rust语言的首选启蒙导师。 基于张老师这份思维导图,咱们将其中的一些分支内容做了具体拆解,或者能够为Rust语言的初学者们解惑,略微升高学习曲线的平缓度。 首先,不论什么样的初学者都须要理解的是Rust语言的一些外围常识,包含: 1、Rust语言是什么样的 2、Rust语言应用领域范畴 3、Rust语言社区 再针对不同特点的开发者,提出针对性的学习倡议: 1、有C语言根底 2、有C++根底 3、有Java/Python/Ruby根底 4、有Go根底 5、有Haskell根底 Rust语言是什么样的Rust最早是Mozilla员工Graydon Hoare的私人打算,说的艰深点,是Hoare的集体业务喜好,谁晓得无心插柳柳成荫,最初却做成了一个广受欢迎的编程开发语言。 Mozilla理解到这个我的项目后就开始资助这个打算,并且在2010年首次揭发了Rust的存在。同一年,其编译器源代码由本来的OCaml语言转移到Rust语言,进行bootstrapping工作,称做Rustc,并于2011年实现。这个可自我编译的编译器在架构上采纳了LLVM做为它的后端。 Rust最为外界称道的是它的稳固和平安,而这种超强的稳定性来源于其独特的内存平安治理。 在C/C++中,很多开发者都会遇到内存方面的问题,Rust严格的编译器和良好的设计使它躲避了这些问题,让它不会呈现内存平安方面的故障。Java和C#的风行是因为它们打消了与内存安全性无关的谬误,但这两种语言(如Java虚拟机和.Net)在运行时应用了垃圾回收从而导致运行开销,进而影响应用程序的性能。 Rust是无垃圾回收机制和Runtime的动态编程语言,它通过新退出的生命周期 (lifetime)和所有权 (Ownership) 个性,实现内存的主动回收,并且无需运行时,避免出现相似内存透露或者悬停指针之类的问题。 Rust的个性决定了它实用于谋求编程语言的速度与稳定性的开发者。在官网领导文档中,如此形容Rust的使命:它最重要的指标是打消数十年来程序员不得不做的衡量:平安与生产力,速度与人机交互的顺畅度(ergonomics)。 简而言之,Rust是C和C ++的间接竞争对手,并且在较小水平上与Java和C#竞争。 至于Rust能不能将前浪拍在沙滩上,恐怕得拉长阵线评估长期利用成果。 Rust适宜哪些应用领域和C语言一样,Rust也算是一种零碎级语言。而且介于Rust不须要在运行时进行垃圾回收,因而它能够提供相似于C和C ++语言的运行时性能。 微软MSRC(微软平安响应核心)首席云开发布道师Ryan Levick曾公开示意:Rust适宜系统性编程,微软大概70%的平安问题是内存平安问题。如果这些软件用Rust写,那么这 70%的平安问题很可能就不存在。 业内有不少预测显示Rust可能会成为下一代操作系统的开发语言,之前火狐就用它编写了一个名为Redox的类UNIX操作系统,微软也有逐步从C/C++转移到Rust构建其根底构造软件的打算。 以后的云计算畛域,Rust也能够蛟龙得水。 在云计算中,须要构建高性能以升高要害开销的基础架构组件。例如,咱们领有虚拟机管理程序,其作用相似于虚拟机管理器,容许在多个租户之间共享单个云资源物理实例。Rust专一于安全性,而不会影响速度和性能。 守业公司也好,互联网大公司也罢,都能够用Rust编写各种工作,比方命令行工具、web我的项目、DevOps工具、嵌入式设施、生物信息学(bioinformatics)、搜索引擎、物联网程序乃至以后大火的机器学习我的项目。 比方Linkerd服务网格代理是在Rust中构建的。 用Rust编写的Qovery Engine是一个凋谢源代码形象层库,可在短短几分钟内于云提供商上轻松部署应用程序。 Dropbox应用Rust重写了其一些外围零碎,而Mozilla应用Rust构建了Firefox浏览器引擎。 除此之外,还有一些开发者基于Rust开发区块链零碎,更多在我的项目中应用Rust的案例能够参考该页面https://prev.Rust-lang.org/zh...。 Rust语言社区一门语言、框架或者库呈现后,人们利用它们,不只是因为本身的弱小,更是因为其背地的生态圈。 社区是培养语言生态的要害载体,它的凋敝也是语言风行的另一个侧面。 如果想疾速入门一门编程语言,绝对应的开源主页和社区是最好的老师,有残缺的代码、官网的领导文档,丰盛的配套资源。任何疑难杂症,Rust语言社区也会有同仁及时解答,大家互相促进,在一个良好的气氛中交流学习Rust。 摆正学习态度在理解了Rust语言根底外围常识之后,其次是要有一个戒骄戒躁的端正学习态度,坐得住冷板凳能力有所成。 首先,Rust的学习曲线还是比拟平缓的,入门门槛绝对较高,所以学习时不要急于求成。 其次,先理解Rust语言概念,再入手实际。磨刀不误砍柴工,吃透根底概念,打好地基,能力搭好房子。 最初,记住编译器是你的老师。Rust难上手很要害的一点就是其严格的编译器,编译器会做尽可能多的查看,争取在编译期就发现问题,打消安全隐患。所以编译器像一个好老师,及早扼杀你的bug,强制性要求你写出能够失常运行的正确代码。 不同类型初学者学Rust要冲破的难点在此基础之上,咱们再看学习Rust要冲破的难点。此处,将初学者分为五大类来探讨。 齐全零根底的开发者:把握计算机根底体系知识结构,了解Rust语言和硬件/OS层的形象,了解Rust语言外围概念、以及它的形象模式,抉择Rust语言的某个实用畛域进行实操训练,通过实际来晋升Rust语言的熟练度和了解深度,同时把握畛域常识。 有C语言根底:因为C语言开发者对高级语言的形象不是很了解,所以着重理解把握Rust所有权机制,包含所有权的语义,生命周期和借用查看。理解Rust语言的形象模式,次要是类型和trait;以及Rust自身的的OOP和函数式语言个性。 有C++根底:C++开发者对于Rust语言的所有权有很好的理解能力,次要精力放在Rust的形象模式和函数式语言个性上。 有Java/Python/Ruby根底:着重了解攻克Rust所有权机制、形象模式、函数式编程语言个性。 有Go根底:Go语言开发者比拟容易了解Rust的类型和trait形象模式,但Go也是GC语言,所以所有权机制和函数式语言个性是他们的学习重点。 有Haskell根底:Haskell系的开发者对Rust语言函数式个性能很好的了解,次要攻克所有权机制和OOP语言个性。 所以,对于有肯定根底的开发者来说,学习Rust语言要把握的几个要害概念有: 1、Rust所有权机制,包含所有权的语义,生命周期和借用查看所有权机制是Rust语言最外围的个性,它保障了在没有垃圾回收机制下的内存平安,所以对于习惯了GC的开发者,了解Rust的所有权是最要害的一环,切记这三点: Rust中的每一个值都有一个被称为其所有者 (owner)的变量。值有且只有一个所有者。当所有者(变量)来到作用域,这个值将被抛弃。这其中又波及到生命周期和借用查看等概念,是绝对比拟难啃的一块硬骨头。 2、Rust语言的形象模式,次要是类型和traittrait借鉴了Haskell中的Typeclass,它是对类型行为的形象,能够艰深地类比为其余编程语言里的接口,它通知编译器一个类型必须提供哪些性能语言个性。应用时要遵循一致性,不能定义互相抵触的实现。 3、OOP语言个性相熟面向对象编程(OOP)的常见的四个个性:对象、封装、继承和多态,能够更好地了解Rust的一些个性,比方impl、pub、trait等等。 4、函数式语言个性Rust语言的设计深受函数式编程的影响,看到函数式个性,数学不好的人可能会望而生畏,因为函数式编程语言的最大特点是把运算过程尽量写成一系列嵌套的函数调用,在Rust中,把握闭包和迭代器是编写函数式语言格调的高性能Rust代码的重要一环。 具体含意和代码阐明不再赘述,大家能够参考《Rust成语设计语言》以及《Rust编程之道》。为了让大家能更通俗易懂地了解Rust语言相干概念,特此附上华为云专家从本身业务教训登程学习总结的Rust材料,点击下载。 最初,感激张汉东老师提供的思维导图学习框架,让初学者能零碎的理解Rust,走近这颗冉冉升起的新星。 书籍下载: Rust语言学习总结.pdf 1.96MB 点击关注,第一工夫理解华为云陈腐技术~ ...

December 31, 2020 · 1 min · jiezi

关于rust:wasm-runtime-性能测试

上一篇文章分享了wasmer runtime,编译好的openssl.wasm文件能够独立运行,这篇文章分享openssl.wasm和原生openssl之间的性能比照。 测试硬件主机 MacBook ProCPU 6-Core Intel Core i7 2.2 GHz/启动超线程内存 16GB磁盘 SSD测试软件Openssl LibreSSL 2.8.3Openssl WASM openssl安装包版本 openssl 1.1.1dWASMER 1.0.0-alpha4测试方法别离对 100/300/500 MB 进行屡次加密解密,失去均值后果。留神,这里有些模式并不是规范的,所以没有全副模仿,仅供参考。 openssl 测试命令 #!/bin/bashkey="A665A45920422F9D417E4867EFDC4FB8"iv="4632527467615238616971576a466653"#file100、file300、file500fileName="file100"ecbEnFileName="ecb-en-${fileName}"ecbDeFileName="ecb-de-${fileName}"cbcEnFileName="cbc-en-${fileName}"cbcDeFileName="cbc-de-${fileName}"ctrEnFileName="ctr-en-${fileName}"ctrDeFileName="ctr-de-${fileName}"gcmEnFileName="gcm-en-${fileName}"gcmDeFileName="gcm-dn-${fileName}"echo "------start openssl-aes-128 encryption-------"echo "------ECB Encryption-------"time openssl enc -aes-128-ecb -in ${fileName} -out ${ecbEnFileName} -K ${key} -pecho "------CBC Encryption-------"time openssl enc -aes-128-cbc -in ${fileName} -out ${cbcEnFileName} -K ${key} -iv ${iv} -pecho "------CTR Encryption-------"time openssl enc -aes-128-ctr -in ${fileName} -out ${ctrEnFileName} -K ${key} -iv ${iv} -pecho "------GCM Encryption-------"time openssl enc -aes-128-gcm -in ${fileName} -out ${gcmEnFileName} -K ${key} -iv ${iv} -pecho "#############################################################"echo "------start openssl-aes-128 Decrypt-------"echo "------ECB Decrypt-------"time openssl enc -aes-128-ecb -d -in ${ecbEnFileName} -out ${ecbDeFileName} -K ${key} -pecho "------CBC Decrypt-------"time openssl enc -aes-128-cbc -d -in ${cbcEnFileName} -out ${cbcDeFileName} -K ${key} -iv ${iv} -pecho "------CTR Decrypt-------"time openssl enc -aes-128-ctr -d -in ${ctrEnFileName} -out ${ctrDeFileName} -K ${key} -iv ${iv} -pecho "------GCM Decrypt-------"time openssl enc -aes-128-gcm -d -in ${gcmEnFileName} -out ${gcmDeFileName} -K ${key} -iv ${iv} -popenssl.wasm 测试命令 ...

December 23, 2020 · 3 min · jiezi

关于rust:劲爆首届中国Rust语言大会终于来了

通过2个月缓和的筹备,咱们兴奋地发表:首届中国Rust语言大会(Rust China Conf 2020),来了! 本次大会将于2020年12月26、27日在深圳深圳湾科技园举办。 大会介绍 Rust China Conf 2020 由 Rust 中文社区(RUSTCC)发动主办、知名企业和开源组织联结协办,是年度国内规模最大的 Rust 线下会议,深受 Rust 中文社区开发者与相干企业的青睐与推崇。 本次大会为线下会议,将于12月26日-27日在深圳举办,预计到场人数 300 左右,面向企业技术负责人、一线工程师及社区集体开发者,特地是已在公司或集体我的项目中实际过 Rust 的开发者。 本次大会将同步开启线上直播,以不便无奈亲临现场的宽广程序员第一工夫观看。并且大会完结后将上传相干录像,用于国内外的开发者长期回顾学习。 本次大会也取得了国内出名开发者社区、媒体与出版社的反对,如CSDN、FCC、思否、掘金、DoraHacks、电子工业出版社、人民邮电出版社等。 大会指标 本次大会,致力于成为中国 Rustaceans 面对面交换的盛宴,为国内的 Rust 开发者和企业提供一次充沛的成绩展现、技术分享、能力晋升、行业资讯交换、企业人才储备建设的机会。 大会议题范畴 本次大会的议题,覆盖面广、讲师阵容强大、议题程度高。 Rust 语言独有的个性,使其触角波及十分宽泛。通过精心筛选来自整个社区和企业的议题投稿,本次大会的主题笼罩如下方面: 云服务中间件分布式计算和存储数据库嵌入式和 IoT 开发OS研发机器人和主动驾驶WebAssembly开发和利用游戏开发区块链开发Rust语言自身深度分析参加分享议题的单位有: 大型互联网IT企业中小型初创型科技公司国内驰名高校社区沉闷开发者残缺的赞助者列表和大会议题列表,请移步大会官网查看: https://2020conf.rustcc.cn 资助通道 大会资助通道目前依然凋谢,请无意资助的企业或集体与Mike分割(vx: daogangtang,请注明“rust大会”),咱们会将资助权利等相干材料发送与您。 售票通道 大会售票通道正式开启: https://www.huodongxing.com/e...(点击文章左下角浏览原文跳转)或者扫描下方二维码关上链接。 这次大会,咱们给 Rustaceans 筹备了 100 张早鸟票,售价仅 299 元!次要面向社区开发者和学生,先到先得!! 热切欢送宽广 Rustaceans 们到场参会,也心愿大家帮忙踊跃宣传,让大会的信息传达到更多开发者那里,推动国内 Rust 社区的倒退! 社区有你的参加,咱们后退更有能源!12月,深圳,咱们不见不散!

December 8, 2020 · 1 min · jiezi

关于rust:稳坐开发领域霸主之位揭秘C语言无可取代的几大原因

摘要:C语言在这过来的五十年间,曾经逐步倒退成为极其重要的软件开发语言。本文将深度分析它是如何与C++、Java、C#、Go、Rust和Python进行竞争,并放弃肯定劣势的。 对于计算机编程语言来说,没有什么技术能沿用半个世纪年,除非它比其余的都好用。C语言于上世纪七十年代初年面世,现在在软件世界仍放弃着底层根底开发的支流语言的位置。 有时,一个技术能被短暂地流传应用,是因为人们还没有找到一个更好的替代品。在过来的几十年,涌现了大量的语言——甚至呈现专门为了挑战C语言的统治位置而设计的新语言。 C真的很难被代替。编程语言考察和软件开发实例都印证了能够用远比应用C语言更好的形式来做开发。但C的位置仍岿然不动,因为它的背地是几十年的积淀和提高。简直没有语言能够在性能、逻辑、或者普遍性上战胜它。 C vs. C++很天然地,人们会拿C与C++做比照,顾名思义,C++是从C倒退而来的。两者之间的不同就在于易扩展性,或者易用性。 语法和形式上,C++与C语言比拟靠近,但C++提供了很多原生C没有的有用个性:命名空间、模板、异样、内存治理。我的项目如果对于性能比拟敏感,例如数据库和机器学习,通常应用C++编写会对提供零碎进步性能更有帮忙。 除此之外,C++比C更容易扩大。C++20甚至带来更多的新个性,包含模块、协程、同步库以及其余概念,这些都让模板更易使用。对规范C的最新订正简直没有新增个性,而是更重视放弃向后兼容性。 事实上,所有C++的长处也是它的毛病。C++的个性应用得越多就越简单,后果就越加难以管制。所以,把本人限度于C++子集的开发者,能防止很多重大的问题。也有些机构想防止C++复杂性,保持应用C。比方Linux内核的开发团队就会避开C++。 对于后期开发者和保护代码人员来说,抉择C是一个防止C++适度应用纠纷的形式。不过,C++也有一系列丰盛的高级性能,该用还是要用。但如果简洁明了更适宜当初或者将来我的项目的整体倒退的话,C会更有劣势。 C vs. Java在JAVA诞生了几十年之后,其依然是个别企业级软件开发的次要语言。大多数优良的企业软件开发我的项目都是用Java编写,包含绝大多数Apache基金会我的项目。当开发企业级我的项目时,Java是一个可行性比拟高的语言。 Java的语法大量地借鉴了C和C++。不过与C不同的是,Java不会默认编译成机器语言。相同地,Java运行时环境JVM会将Java代码即时编译到指标环境中运行。在良好的条件下,即时编译的Java能够达到甚至超C的性能。 Java奉行的“一次编写,随处运行”的思维,Java程序仅需小的调整,就能运行在不同的环境。相比之下,只管C曾经移植到许多体系结构中,然而任何给定的C程序可能依然须要定制能力在Windows和Linux上失常运行。 其可移植性和弱小性能的联合,以及软件库和框架的宏大生态系统,使Java成为企业级我的项目语言的一员。 然而,Java落后C的中央在于,Java素来没有竞争的畛域,都是靠近底层运行,或间接操作硬件。C代码被转换成机器码,由过程间接执行。Java被编译成字节码,由JVM解释器转换为机器代码的中间代码。此外,只管Java的主动内存治理在大多数状况下是一种坏事,然而C更适宜于对无限内存资源有优化要求的程序。 在某些状况下,Java的性能能够靠近C。JVM的JIT引擎能在运行时依据程序的行为优化程序,能够进行许多品种的优化,对于事后编辑的C语言而言,这个是行不通的。例如,ApacheSpark应用自定义的内存治理代码绕过JVM进行了肯定水平的内存内解决优化。 C vs. C# 与 .Net在推出近20年之后,C和.NET框架依然是企业软件世界的次要组成部分。有人说,C#和.NET是微软对Java的一种回应(托管代码编译系统和通用的运行时),因而C和Java之间的许多比拟也实用于C和C#/.NET。 与Java(以及Python的某些局部)一样,.NET提供了跨多种平台的可移植性和集成软件的广大生态系统。思考到.NET世界中的一些面向企业的开发,这些都是很大的劣势。当应用C或任何其余.NET语言开发程序时,能够利用针对.NET运行时编写的各种工具和库。 .NET另一个和Java相似的长处是JIT优化。C和.NET程序能够像C那样提前编译,.NET运行时可即时编译,并能应用运行时的信息进行优化。JIT编译容许对正在运行的.NET程序进行各种优化,这在C中是无奈进行的。 和C一样,C和.NET提供了各种间接拜访内存的机制。堆、堆栈和非托管零碎内存都能够通过.NETapi和对象进行拜访。开发人员能够应用.NET中的unsafe模式来实现更高的性能。 不过,值得注意的是,托管对象和unsafe对象之间不能随便替换,它们之间的封装传送须要以升高性能作为代价。因而,缩小两者之间的传递,能够最大化的进步.NET程序的性能。 当累赘不起托管内存绝对于非托管内存的代价时,或者当.NET运行时对于指标环境(如内核空间)是一个很蹩脚的选择项或者基本不可用时,那么C语言或者就能解决问题了。与C和.NET不同,C默认状况下会开启间接内存拜访。 C vs. GoGo语法和C很像,大括号作为分隔符、以分号结尾语句。精通C的开发人员通常无需太多艰难就能够间接转入Go,甚至把Go的新个性如名称空间和包治理思考在内也是如此。 代码的易读性是Go的领导设计指标之一,开发人员可能轻松地跟上任何Go我的项目的速度,并在短时间内精通代码库。C代码库很难摸索,因为它们很容易变成一个由宏和特定于我的项目或团队的嵌套。Go的语法,以及其内置的代码格局和项目管理工具,都是为了防止这些机制问题。 Go还提供额定的性能,像Goroutines和Channels,用于解决并发性的语言级工具以及组件之间的消息传递。在C语言外面只能本人实现或者用三方库,然而Go以开箱即用的形式提供了这些个性,让咱们在开发须要相似性能的软件的时候,变得极其不便。 在内存治理方面,Go与C有较大区别。默认状况下,Go对象被主动治理和回收。对于大多数编程工作来说,这十分不便。但这也意味着任何须要对内存进行非凡解决的程序,会比拟难办。 Go确实蕴含了一个unsafe的包,用于躲避Go的一些类型解决的安全性问题,例如应用Pointer类型读取和写入任意内存。但unsafe伴有一个正告,即用它编写的程序“可能不可移植,并且不受Go1兼容性准则爱护”。 Go非常适合构建命令行程序和网络服务等程序,因为它们很少须要这样的细粒度操作。然而低级的设施驱动、内核空间操作系统组件以及其余须要对内存布局和治理进行严格控制的工作最好是在C中创立。 C vs Rust在某些方面,Rust是解决C和C++造成的内存治理难题的新计划,也是解决这些语言许多其余毛病的新计划。Rust编译为本机代码,因而在性能上与C相当。不过,默认状况下,内存平安是Rust的次要卖点。 Rust的语法和编译规定能帮忙开发者防止常见的内存治理谬误。如果一个程序存在跨过Rust语法的内存治理问题,那么它就不会编译。应用该语言的老手,尤其是从像C这样为此类谬误提供了大量空间的语言转过来的老手,他们学习Rust的第一阶段是如何安抚编译器。然而Rust支持者认为,这种短期的苦楚将失去一个长期的回报:不会就义速度的更平安的代码。 Rust也能够用它的工具改善C。默认状况下,我的项目和组件治理是Rust提供的工具链的一部分,与Go雷同。有一种默认的、举荐的形式来治理包、组织我的项目文件夹,以及解决许多其余事件,这最多是长期措施,每个我的项目和团队解决它们的形式都是不同的。 尽管如此,对于C开发人员来说,被吹捧为Rust劣势的货色可能看起来不是那样的。Rust的编译时平安个性不能被禁用,所以即便是再小的Rust程序也必须合乎Rust的内存平安限度。默认状况下,C可能不太平安,但在必要时,它更灵便,更宽容。 另一个可能的毛病是Rust语言的体积。即便思考到规范库,C的新个性也绝对较少。Rust个性集正在蔓生并持续增长。与C++相比,较大的Rust个性集意味着更弱小的能力,但也更简单。C是一种较小的语言,但更容易建模,因而可能更适宜于看上去有点臃肿的我的项目中。 C vs Python当初,每当议论软件开发时,Python仿佛总是会被人们提起。毕竟这是“第二个适宜所有事件的语言”,毫无疑问,它是最通用的语言之一,有数千个第三方库。 Python强调的是开发速度而不是执行速度,这是它与C的最大区别。用C语言组装一个程序可能须要一个小时,而用Python只需几分钟。另一方面,该程序在C语言中执行可能只须要几秒钟,而在Python中运行则须要一分钟。然而对于古代硬件来说,Python足够快,这是它获得成功的要害。 另一个次要区别在于内存治理。Python程序齐全是由Python运行时进行内存治理,因而开发人员不用放心调配和开释内存的艰难。但这里有必要讲明,开发者的轻松是以就义运行时性能为代价的,编写C程序须要审慎地留神内存治理。 其实,Python和C之间有一个很深的分割:参考Python运行时是用C写的。这容许Python程序打包C和C++编写的库。Python生态系统中一些重要的第三方库,如机器学习,其外围是C代码。 如果开发速度比执行速度更重要,程序执行局部能够隔离成独立的组件,而不是扩散在整个代码中,那么纯Python或Python和C库的混合比独自应用C更好。否则的话,C依然是霸主。 感激XJRsoft提供撰文反对,原文来自Serdar Yegulalp 详情参阅:https://www.xjrsoft.com 点击关注,第一工夫理解华为云陈腐技术~

December 3, 2020 · 1 min · jiezi

关于rust:为什么需要-libp2prs

Netwarps 始终在做去中心化的存储和计算平台,指标是物联网,甚至 NDN 网络。因而咱们须要构建一个简洁的高性能的底层网络。之前 Netwarps 应用 C 语言实现了一套根本的 P2P 网络,而后在这个网络上搭建了去中心化文件存储系统(DFS)和去中心化计算平台(DCP)。 然而随着技术的倒退,咱们越来越感觉基于 C 语言构建的 P2P 底层网络存在很多问题:1、C 语言实现的异步框架保护艰难,代码逻辑简单;2、C 语言在程序员的眼里里仿佛正在失去吸引力;3、C 语言与当下风行的很多设计形式,编程形式存在代沟,难以与其余我的项目造成良性互动。 于是,咱们开始思考应用更新的语言来实现一套异步的,代码简洁的,运行高效的 P2P 网络。 而这时,咱们第一个就想到了 libp2p,作为区块链行业的明星我的项目,Go 语言的 libp2p 当初简直成了去中心化网络的一个标杆。咱们本人对 libp2p 的应用其实很早就开始了,然而咱们却始终没有应用在咱们本人的去中心化存储和计算平台里,起因只有一个,Go 语言是一个运行时比拟重的语言,尽管 Go 适配了 Arm 等低功耗设施,然而其对硬件的要求,远不是咱们思考的 IOT 设施能达到的。 当咱们起心开始做一个新的 P2P 网络的时候,咱们基本上就确定了用 Rust 来实现。一方面,很多 IOT 设施曾经兼容了 Rust,另一方面,没有运行时的引诱是任何一个把眼光放在 IOT 方面的团队回绝不了的,这象征这我的程序能够在更低功耗的硬件上运行。 然而,这条路并不好走。 首先,大家低估了 Rust 语法的难度,咱们的团队次要是 C 语言和 C++ 语言功底不错的程序员,在咱们看来,还有什么语言的语法会比这两个老古董更繁琐吗?很快就被打了脸。 其次,Rust 的生态对于入门玩家不太敌对。咱们有共事说,当我编译出错的时候,我甚至不晓得是我的问题还是编译工具的问题。 再次,Rust 对程序员的约束性很强,尽管这一点在咱们开始这个我的项目之前也曾有所耳闻,然而咱们真没想到,这个适应的过程会这么长。 还有,Rust 的异步框架成熟度都不太高,应用起来磕磕绊绊,有时候遇到问题也会莫衷一是。 然而,好在最初咱们实现了。实现 libp2p-rs 对咱们的团队是一个相当的胜利也是一个相当的考验。 咱们深知 libp2p-rs 这个我的项目不是完满的甚至可能都说不上欠缺,但咱们保持把它开源进去,一方面算是一个咱们向 Rust 社区的些微奉献,另一方面,咱们心愿通过这个形式让更多的程序员尤其是中国程序员关注 Rust,关注 P2P。同时咱们也心愿大家可能理解,去中心化技术不仅仅是区块链和各种 Coin,去中心化技术也是 IOT 甚至整个互联网的将来方向。 ...

November 5, 2020 · 1 min · jiezi

关于rust:64位-WASMWASM-FaaSWASM-与AI-的结合Kotlin-将支持WASM-WASM周报1014

编者按:一WebAssembly 在 Serverless 和云计算畛域一直攻城略地,咱们看到了64位WebAssembly、基于WebAssembly的 serverless、以及WebAssembly 在 AI 畛域的畛域。 WebAssembly64 位 WebAssembly64位 WebAssembly 的 Memory64 是它正在逐渐实现! 64位 CPU 是当今的支流。 Memory64 为 WebAssembly 虚拟机提供了更多可用的内存空间。它非常适合服务端利用。 Second State 公布基于 WebAssembly 的serverless FaaSSecond State 函数即服务 (FaaS) 是一个高性能 serverless 服务。它反对 Rust 编程语言,Rust 在这下面是一等公民。基于 WebAssembly, Second State FaaS 比虚拟机或者基于容器的替代品冷启动和运行更快。它非常适合计算密集型应用程序,如媒体解决、数据分析和边缘 AI / Tensorflow 利用。 The Second State FaaS 仍处于测试阶段。在测试期间能够收费部署和应用 Rust 函数作为服务。无需注册,也不须要信用卡。创立一个动态网页并将FaaS 用作后端服务。 快来试试吧.。 WebAssembly 提供的 AI 服务过来的两周中,咱们见到了两种通过 WebAssembly 运行 AI 模型的办法。一种是将 TensorFlow 模型压缩并编译为 WASM 程序,另一种是提供相似 WASI 的命令 API来在 GPU上原生运行TensorFlow 模型。你喜爱哪种办法呢! ...

October 14, 2020 · 2 min · jiezi

关于rust:如何在-Rust-中使用-MQTT

Rust 是由 Mozilla 主导开发的通用、编译型编程语言。该语言的设计准则为:平安、并发、实用,反对 函数式、并发式、过程式以及面向对象的编程格调。Rust 速度惊人且内存利用率极高。因为没有运行时和垃圾回收,它可能胜任对性能要求特地高的服务,能够在嵌入式设施上运行,还能轻松和其余语言集成。Rust 丰盛的类型零碎和所有权模型保障了内存平安和线程平安,让您在编译期就可能打消各种各样的谬误。 MQTT 是一种基于公布/订阅模式的 轻量级物联网音讯传输协定 ,能够用极少的代码和带宽为联网设施提供实时牢靠的音讯服务,它广泛应用于物联网、挪动互联网、智能硬件、车联网、电力能源等行业。 本文次要介绍如何在 Rust 我的项目中应用 paho-mqtt 客户端库 ,实现客户端与 MQTT 服务器的连贯、订阅、勾销订阅、收发音讯等性能。 我的项目初始化本我的项目应用 Rust 1.44.0 进行开发测试,并应用 Cargo 1.44.0 包管理工具进行项目管理,读者可用如下命令查看以后的 Rust 版本。 ~ rustc --versionrustc 1.44.0 (49cae5576 2020-06-01)抉择 MQTT 客户端库paho-mqtt 是目前 Rust 中,功能完善且应用较多的 MQTT 客户端,最新的 0.7.1 版本反对 MQTT v5、3.1.1、3.1,反对通过规范 TCP、SSL / TLS、WebSockets 传输数据,QoS 反对 0、1、2 等。 初始化我的项目执行以下命令创立名为 mqtt-example 的 Rust 新我的项目。 ~ cargo new mqtt-example Created binary (application) `mqtt-example` package编辑我的项目中的 Cargo.toml 文件,在 dependencies 中增加 paho-mqtt 库的地址,以及指定订阅、公布代码文件对应的二进制文件。 ...

October 10, 2020 · 4 min · jiezi

关于rust:Rust-的包管理工具-Cargo

Cargo 是 Rust 工具链中内置的构建零碎及包管理器。它能够解决泛滥诸如构建代码、下载编译依赖库等琐碎但重要的工作,绝大部分的Rust用户都会抉择它来治理本人的Rust我的项目。 创立我的项目治理 Rust 我的项目及其模块依赖(Crate) 查看 Cargo 版本$ cargo --version创立一个我的项目$ cargo new hello_cargo执行完以上命令后,Cargo 会以同样的名字创立我的项目目录并搁置它生成的文件。进入 hello_cargo 文件夹,能够看到 Cargo 刚刚生成的两个文件与一个目录:一个名为 Cargo.toml 的文件,以及一个名为 main.rs 的源代码文件,该源代码文件被搁置在 src 目录下。同时,Cargo 还会初始化一个新的 Git 仓库并生成默认的 .gitignore 文件 Cargo.tomlcargo.tlml 是生成的规范配置文件, 内容如下: [package]name = "hello_cargo"version = "0.1.0"authors = ["Your Name <you@example.com"]edition = "2018"[dependencies]最初一行文本中的 [dependencies] 是一个区域标签,它表明随后的区域会被用来申明我的项目的依赖。 Cargo 会默认把所有的源代码文件保留到src目录下,而我的项目根目录只被用来寄存诸如README文档、许可申明、配置文件等与源代码无关的文件。 构建和运行我的项目编译程序在以后的 hello_cargo 我的项目目录下, 执行编译: $ cargo build Compiling hello_cargo v0.1.0 (/Users/Herbert/rust/projects/hello_cargo) Finished dev [unoptimized + debuginfo] target(s) in 0.21s会将可执行程序生成在门路 target/debug/hello_cargo。 ...

October 7, 2020 · 1 min · jiezi

关于rust:Azure-Static-Web-Apps支持WASMRust-2021-的看法WebAssembly-周报1001

编者按: WebAssembly 在 Serverless 和云计算畛域一直攻城略地,许多 Rust发烧友分享了他们2021年对 Rust 的冀望。 大家中秋高兴呀 WebAssemblyAzure 动态网络应用反对 WebAssemblyAzure Static Web Apps 当初反对带有无服务器 Azure 函数反对的 Blazor WebAssembly! 基于 Rust 和 WebAssembly 的 Node.js 图片分类作为服务本文教您如何应用 Rust、 WebAssembly 和 TensorFlow 为 MobileNet 模型创立 web 服务。风行的基于 MobileNet 的 TensorFlow 模型被用来分类图片。但如何从 WebAssembly 外在GPU上执行tensorflow模型? 看看这篇文章是这么说的! WebAssembly 可能是云原生伸缩性的要害Solo 的 CEO Idit Levine 解释了 WebAssecmbly 在 entruent proxy 和 Solo.io 中的应用,该公司的新提议是将 WASM 模块打包成凋谢容器倡导(Open Container Initiative)格局。她提到 WebAssembly 将是将来云计算基础设施的重要一环。 Hidamari,一个用于运行 WebAssembly 代码的古代操作系统Hidamari 是一个次要面向运行 WebAssembly 字节码的操作系统,字节码能够通过 WASI 接口调用该操作系统函数。这个我的项目正处于晚期开发阶段。 ...

October 1, 2020 · 2 min · jiezi

关于rust:使用Rust和WebAssembly在Nodejs中进行人脸检测

本文将介绍如何编写基于Node.js的AI即服务应用程序。 当今,用于AI的支流编程语言是Python。然而,用于Web的编程语言是JavaScript。为了将AI性能作为Web服务提供,咱们须要将AI算法包装在JavaScript中,尤其是Node.js。 然而,Python和JavaScript自身都不适宜计算密集型AI应用程序。它们是具备沉重运行时的高级(即慢速)语言。它们的易用性以升高性能为代价。 Python通过将AI计算包装在本机C / C ++模块中来解决此问题。 Node.js能够做同样的事件,然而咱们有一个更好的办法 --WebAssembly。 WebAssembly VM提供与Node.js和其余JavaScript运行时的严密集成。它们具备高性能,内存平安,默认状况下平安且可跨操作系统移植的特点。然而,咱们的办法联合了WebAssembly和本机代码的最佳性能。 工作原理基于Node.js的AI即服务应用程序由三局部组成。 Node.js应用程序提供Web服务并调用WebAssembly函数以执行计算密集型工作,例如AI推理。数据筹备,解决以及与其余零碎的集成是通过WebAssembly 函数实现的。最后,咱们反对Rust。应用程序开发人员必须编写此函数。AI模型的理论执行是通过原生代码实现的,以最大限度地进步性能。该代码的这一部分十分简短,并通过了安全性和安全性审查。应用程序开发人员只需从WebAssembly函数调用此原生程序,就像明天在Python和Node.js中应用原生函数的形式一样。 接下来,咱们看下示例程序。 人脸检测示例人脸检测Web服务 容许用户上传照片,并显示绿色框标记的图像。 用于执行MTCNN人脸检测模型的Rust源代码基于Cetra的教程:应用Tensorflow Rust进行人脸检测。咱们进行了更改以使Tensorflow库在WebAssembly中工作。 Node.js利用程序处理文件上传和响应。 app.post('/infer', function (req, res) { let image_file = req.files.image_file; var result_filename = uuidv4() + ".png"; // Call the infer() function from WebAssembly (SSVM) var res = infer(req.body.detection_threshold, image_file.data); fs.writeFileSync("public/" + result_filename, res); res.send('![]()');});如您所见,JavaScript应用程序仅将图像数据和一个名为detection_threshold的参数传递给infer()函数,该参数指定要检测的最小脸部,而后将返回值保留到服务器上的图像文件中。 infer()函数用Rust编写,并编译成WebAssembly,以便能够从JavaScript调用它。 infer()函数将输出图像数据展平为一个数组。它建设了一个TensorFlow模型,并应用扁平化的图像数据作为模型的输出。 TensorFlow模型执行将返回一组数字,这些数字批示每个面框的四个角的坐标。而后,infer()函数在每个脸孔四周绘制一个绿色框,而后将批改后的图像保留到Web服务器上的PNG文件中。 [wasm_bindgen]pub fn infer(detection_threshold: &str, image_data: &[u8]) -> Vec<u8> { let mut dt = detection_threshold;... ...let mut img = image::load_from_memory(image_data).unwrap();// Run the tensorflow model using the face_detection_mtcnn native wrapperlet mut cmd = Command::new("face_detection_mtcnn");// Pass in some argumentscmd.arg(img.width().to_string()) .arg(img.height().to_string()) .arg(dt);// The image bytes data is passed in via STDINfor (_x, _y, rgb) in img.pixels() { cmd.stdin_u8(rgb[2] as u8) .stdin_u8(rgb[1] as u8) .stdin_u8(rgb[0] as u8);}let out = cmd.output();// Draw boxes from the result JSON arraylet line = Pixel::from_slice(&[0, 255, 0, 0]);let stdout_json: Value = from_str(str::from_utf8(&out.stdout).expect("[]")).unwrap();let stdout_vec = stdout_json.as_array().unwrap();for i in 0..stdout_vec.len() { let xy = stdout_vec[i].as_array().unwrap(); let x1: i32 = xy[0].as_f64().unwrap() as i32; let y1: i32 = xy[1].as_f64().unwrap() as i32; let x2: i32 = xy[2].as_f64().unwrap() as i32; let y2: i32 = xy[3].as_f64().unwrap() as i32; let rect = Rect::at(x1, y1).of_size((x2 - x1) as u32, (y2 - y1) as u32); draw_hollow_rect_mut(&mut img, rect, *line);} let mut buf = Vec::new();// Write the result image into STDOUTimg.write_to(&mut buf, image::ImageOutputFormat::Png).expect("Unable to write");return buf;}face_detection_mtcnn命令以原生代码运行MTCNN TensorFlow模型。它蕴含三个参数:图像宽度,图像高度和检测阈值。从WebAssembly infer() 通过STDIN传入RGB值的理论图像数据。模型的后果以JSON编码,并通过STDOUT返回。 ...

October 1, 2020 · 5 min · jiezi

关于rust:Line在服务端-WASM的分享用Rust和wasm实现-AI-as-a-serviceWebAssembly-周报0923

WebAssembly 周报 #19入群请至文末编者按: WebAssembly 持续蓬勃成长,有了更多语言反对、更多工具以及更多的利用案例。 WebAssembly Today用 Node.js写的高性能且平安的人工智能即服务( AI as a Service) ???? 对于1200万 JavaScript 开发者来说,AI as a Service 意味着在 Node.js 中运行 Tensorflow 模型。做到这点的一种办法是用在 Node.js 里运行的 Rust 和 WebAssembly 替换 Python 本机模块。本文将教您如何应用 Rust、 WebAssembly 和 Tensorflow 实现作为 web 服务的人脸检测。 Emscripten 2.0.4 公布,反对 Bazel ????Emscripten 2.0.4 本周公布了。新性能: 是反对 Bazel 的第一个版本。进行了默认蕴含 malloc 和 free。如果您须要从 JS 拜访它们,则必须应用 -s EXPORTED_FUNCTIONS=['_malloc', ..]手动导出。进行在 -O1 中运行 Binaryen 优化。这使得 -O1 构建得略微大一些,然而它们编译得更快,这在“有斗争的”构建(在-O0和适宜公布构建的更高优化级别二者之间)中更有意义。增加 ERROR_ON_WASM_CHANGES_AFTER_LINK 选项,如果连贯后咱们须要在 wasm-emscripten-finalize 或者 wasm-opt 中执行任何工作,则增加 ERROR on wasm changes after _ link 选项。这能够验证链接是达到最快速度,也不做 DWARF 重写。Blazor 速度更快了 (即使没有 AOT) ???? ...

September 23, 2020 · 2 min · jiezi

关于rust:Rust-模块系统

Rust模块零碎参考文章后作出的简略总结,如有谬误脱漏,望指出! 总结: Rust编译器不会依据文件树主动构建模块树,须要开发者手动在用mod关键字构建子模块关系。所有文件都晓得crate, 代表src/main.rs, 互相调用须要用到crate.通过pub关键字设置内容是否导出。依赖: proj |__ main.rs |__ conf.rs |__ sub_mod_1 |__ a.rs |__ mod.rs |___ sub_mod_1 |__ b.rs |__ mod.rs三种依赖及其解决办法: main.rs -> conf.rs, 在main.rs中申明mod conf;, 则可调用conf.rs的内容。 main.rs -> sub_mod_1/a.rs, 在sub_mod_1/mod.rs中退出mod a; 来构建模块关系,随后main.rs中退出mod sub_mod_1;即可。 sub_mod_1/a.rs -> sub_mod_2/b.rs, 相似下面,各自的mod.rs中退出mod构建模块关系后, a.rs通过crate::sub_mod_2::b来调用b的内容。 参考资料https://zhuanlan.zhihu.com/p/164556350http://www.sheshbabu.com/posts/rust-module-system/https://doc.rust-lang.org/nightly/edition-guide/rust-2018/module-system/path-clarity.html

September 17, 2020 · 1 min · jiezi

关于rust:无服务-wasm-wasm-Referencewasm-运行时-WebAssembly-周报0902

退出 WebAssembly 中文群,请至文末编者按: 本周的 WebAssembly 新闻多多! WebAssemblyServerless Rust + WebAssembly 本文通知你如何应用 Rust + WebAssembly 创立机器学习和数据可视化的 serverless 利用 。 embed-wasmembed-wasm crate 容许开发者用 Rust 写端到端的利用。后端被编译为本地 Web 服务器应用程序,前端被编译为 wasm 并从后端提供给浏览器(因而将前端“嵌入”在后端中)。 Wasmtime 中的 WebAssembly Reference 类型WebAssembly reference 类型来啦! 这篇博客文章解释了 WebAssembly reference 是什么,以及如何启用从非 JavaScript 主机调用 WebAssembly 函数。 对于服务端的 WebAssembly 而言,这是十分重要的提高。 Motoko, 一个能间接用在互联网的编程语言Motoko 由 Difinity 团队设计开发,专门针对 WebAssembly 设计。这篇发表在 Stackoverflow 上的博客文章解释了创造 Motoko 的起因。 Motoko 是用于计算机互联网(基于 WebAssembly 的去中心化云计算平台)的最佳语言。 Ring 语言下次降级将专一 WebAssemblyRing 是一个用于通用目标的编程语言。它反对命令式、步骤式、面向对象、函数式和申明式编程范例。 Ring 打算在9月公布的1.13版本中反对 Qt WebAssembly。 Qt WebAssembly 是一个 平台插件,用于创立可一集成到 web 页面中的 Qt 利用。 ...

September 2, 2020 · 2 min · jiezi

关于rust:小小的分页引发的加班血案

问题剖析通过以上的对话,身为程序员的你是否也遇到过妹子这样的问题呢?传统的而且网上到处充斥着的也是这类形式,客户端依据本人的滚动一直的更新pagesize和pageindex两个参数,而后上传给服务端接口获取数据,而且网络上也很少阐明这种形式是否有问题,那到底有没有问题呢? 谈到分页,无论程序怎么写,分页这个业务的外围动作是依据开始地位和完结地位来获取一段数据,无论你的排序规定有多简单,最终的目标总是获取总列表数据中一段间断的数据。无论你是间接用的sql语句分页,还用的搜索引擎(比方es),最终在客户端体现的成果就是下一页的数据展示。 当然体现在客户端的UI上的交互操作能够有很多款式 如果是瀑布流或者app段滚动展现的形式,或者其余不须要数据总个数的状况下,菜菜认为服务端千万不要查问这个总个数数据,展现方齐全能够以下一页有无数据作为是否持续拉取下一页数据的根据。话题回归,如果客户端根据pagesize和pageindex参数来进行分页需要,有没有问题呢?当然有,要不然菜菜写这篇文章意义何在,我又不是一个喜爱爱扯淡的程序员~~ 问题所在这里以最简略也是最根本的sql 语句分页为例,如果当初数据库现有数据为 1,2,3,4,5,6,7排序的规定是依照大小倒序,即数据的全副列表为: 7,6,5,4,3,2,1如果当初是获取第二页数据,pagesize为2,pageindex为2,正确后果为 “5,4” 。这无可非议,在数据未产生扭转的状况下,正确后果的确如此,那如果数据产生的变动呢,如果当初新退出一条数据 8,列表数据会变为 8,7,6,5,4,3,2,1那根据以上分页准则,第二页获取的数据就变为了“6,5”,聪慧的你是不是发现了问题,这也可能是D妹子引发加班的起因。 分页的操作是建设在动态数据上的操作解决问题分页操作的数据源是动静变动的,有时候变动的局部正好产生在你获取的数据范畴内,就会产生数据反复或者谬误的状况。那怎么解决呢? 客户端作为数据的需求方和展现方,客户端须要记住曾经加载的数据的主键列表,如果某条数据曾经展现过,依据业务需要来确定是否要反复展现,个别状况下须要去重。 如果数据量十分大,客户端保护一个数据池的计划其实也不够现实服务端服务端分页接口参数新增上一页最初一条数据id参数lastId,去掉pageindex参数,因为在少数状况下,pageindex参数在服务端的作用是确定数据的终点而已,如果有了lastid,pageinde在很多状况下其实曾经不须要了。服务端把所有的数据做缓存,这样动态数据在肯定工夫内动态化,然而这样也是治标不治本。如果业务上对于排序无要求的话,服务端能够采纳程序分页,把获取的数据落在不会变动的数据段上服务端要想把动静的数据搞成动态有点难度业务方无论程序怎么优化也扭转不了数据是在不停变动的实质,如果业务方(产品,经营)可能承受数据在偶然状况下能反复的景象,那能大幅度缩小程序员的工作了。 有时候你认为的数据bug,在其余业务部门不肯定是什么重大问题支付架构师进阶材料大礼包

August 30, 2020 · 1 min · jiezi

关于rust:WebAssembly-和-Rust-社区对-Mozilla-裁员做出回应WebAssembly-一周一报-0826

编者按: WebAssembly 和 Rust 社区对 Mozilla 的裁员做出回应,并摸索后退的路线。 WebAssemblyWebAssembly 之父 Alon Zakai 在Mozilla 事件之后对 WebAssembly 工具化的将来示意乐观 ????Zakai 示意,尽管 Mozilla 逐步缩小了对 WebAssembly 工具化的投资,然而开源社区迎接了挑战,当初负责了大部分工作。Mozilla 上一轮裁员次要影响了 Rust Wasm 工具化团队,但围绕这些工具曾经有了一个生机勃勃的社区。 应用WebAssembly在Web浏览器中预览PineTime表盘 ⌚️ 该我的项目是 Pinetime 智能手表的固件。用古代 C++ 编写的,而后编译为 WebAssembly 以在浏览器中运行。点击这里查看如何在浏览器中获取 Pinetime 智能手表表盘。 技术: 用 C++ 写的代码基于 CMake 创立的零碎。基于 FreeRTOS 10.0.0 实时操作系统应用 LittleVGL/LVGL 6.1.2 作为 UI 库... 以及 NimBLE 1.3.0 作为 BLE 栈欧盟KubeCon:Envoy 将应用 WebAssembly 扩大监控微服务 ????Tetrate 的软件工程师 Yaroslav Skopets 在 KubeCon 介绍了其公司开发的 Envoy 扩大,以反对 WebAssembly 函数。Envoy 扩大应用 AssemblyScript,而后将其编译成 WebAssembly 字节码。 ...

August 26, 2020 · 2 min · jiezi

关于rust:Rust学习总结-写-Rust-函数免费拿树莓派

学 Rust,收费拿树莓派 本次学习Rust真是从0开始,之前从未应用过虚拟机,也从未独立搭建过Docker环境;废话不多说,咱们开始吧 # Rust 三问Rust是什么? Rust 是一门零碎编程语言(Systems Programming Language),兼顾平安(Safety)、性能(Speed)和并发(Concurrency)。 Rust能够用来干啥? Rust作为一门底层的零碎编程语言,实践上,应用 C/C++ 的畛域都能够应用Rust实现,例如对硬件须要精密管制的嵌入式编程、对性能要求极高的应用软件(数据库引擎、浏览器引擎,3D渲染引擎等)。 Rust和其余同类型的语言有什么劣势? 绝对于 C/C++ 的系统性缺点(内存治理不当造成的安全漏洞),Rust通过所有权(Ownership)机制在编译期间确保内存平安,无需垃圾回收(Garbage Collection, GC),也不须要手动开释内存。 # 搭建Rust环境的整体思路办法A: 如果是Win7/8 能够通过部署虚拟机,虚拟机再装置 Ubuntu Server 20.04 TLS和Rust,如果是Win10则能够抉择在利用市场装置Ubuntu Server 20.04 TLS。办法B: 应用 Docker这里我应用的是Ubuntu虚拟机搭建Rust。 # 虚拟机Ubuntu环境搭建Rust# Install Rust# 既然咱们要用 Rust 写函数,也须要装置 Rust 语言的编译器与工具。# 如果感觉慢,能够应用中科大镜像,应用办法请自行搜寻$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh$ source $HOME/.cargo/env# Install Node.js$ curl -sL https://deb.nodesource.com/setup_14.x | bash$ apt-get install -y nodejs# Install ssvmup toolchain# 最初,ssvmup 工具主动执行构建过程并生成所有工件,使 Node 应用程序能够轻松调用 Rust 函数。同样,须要装置 ssvmup 依赖项。$ npm install -g ssvmup # Append --unsafe-perm if permission denied# OS dependencies for SSVM$ sudo apt-get update$ sudo apt-get -y upgrade$ sudo apt install build-essential curl wget git vim libboost-all-dev# Install the nodejs addon for SSVM$ npm install ssvm# 第一个Rust程序fn main() { let txt = "Hello, world!"; println!("I say : {}", txt);}应用fn申明函数,和JavaScript倒是大不相同。main()是Rust程序的默认入口,println!示意打印文本到控制台。 ...

August 26, 2020 · 2 min · jiezi

关于rust:rust学习读取文件第一次使用lib文件

第一步:cargo new minigrep 创立一个miigrep初始化我的项目 第二步:编辑lib.rs文件: use std::fs;use std::error::Error;#[derive(Debug)]pub struct Config<'a> { pub query: &'a str, pub filename: &'a str,}impl Config<'_>{ pub fn parse_config1(args: &[String]) -> Result<Config, &'static str> { if args.len() < 3 { panic!("门路谬误"); } let query = &args[1]; let filename = &args[2]; Ok(Config { query, filename }) }}pub fn run(config: Config) -> Result<(), Box<dyn Error>>{ let contents = fs::read_to_string(config.filename)?; println!("With text:\n{}", contents); Ok(())}第三步:编辑main.rs文件: use std::env;use std::process;use minigrep::Config;fn parse_config(args: &[String]) -> Config { let query = &args[1]; let filename = &args[2]; Config {query, filename}}fn main(){ let args: Vec<String> = env::args().collect();// let config = parse_config(&args); let config = Config::parse_config1(&args).unwrap_or_else(|err| { println!("Problem parsing arguments: {}", err); process::exit(1); });; println!("{:?}", config); println!("Searching for {}", config.query); println!("In file {}", config.filename);// run(config); if let Err(e) = minigrep::run(config) { println!("错误信息: {}", e); process::exit(1); }// let text = fs::read_to_string("C:\\Users/kunt/Desktop/rgba.txt").unwrap();// println!("读取到的文件内容:\n{:?}", text);}如此就能够读取正确文件名称的文件了: ...

August 13, 2020 · 2 min · jiezi

关于rust:微软看上的Rust-语言安全性真的很可靠吗

摘要:近几年,Rust语言以极快的增长速度取得了大量关注。其特点是在保障高安全性的同时,取得不输C/C++的性能。在Rust被很多我的项目应用当前,其理论安全性体现到底如何呢?近几年,Rust语言以极快的增长速度取得了大量关注。其特点是在保障高安全性的同时,取得不输C/C++的性能,让零碎编程畛域难得的呈现了充满希望的新抉择。在Rust被很多我的项目应用当前,其理论安全性体现到底如何呢?往年6月份,来自3所大学的5位学者在ACM SIGPLAN国内会议(PLDI'20)上发表了一篇研究成果,针对近几年应用Rust语言的开源我的项目中的平安缺点进行了全面的考察。 这项钻研考察了5个应用Rust语言开发的软件系统,5个被宽泛应用的Rust库,以及两个破绽数据库。考察总共波及了850处unsafe代码应用、70个内存平安缺点、100个线程平安缺点。 在考察中,研究员不光查看了所有破绽数据库中报告的缺点和软件公开报告的缺点,还查看了所有开源软件代码仓库中的提交记录。通过人工的剖析,他们界定出提交所修复的BUG类型,并将其归类到相应的内存平安/线程平安问题中。所有被考察过的问题都被整顿到了公开的Git仓库中:https://github.com/system-pclub/rust-study 内存平安问题的剖析这项钻研考察了70个内存平安问题。针对于每个问题,研究者认真的剖析了问题呈现的根因(cause)和问题导致的成果(effect)。问题根因是通过批改问题时提交的patch代码来界定的——即编码的谬误产生在哪儿;问题的成果是指代码运行造成可察看的谬误的地位,比方呈现缓冲区溢出的代码地位。因为从根因到成果有个传递过程,这两者有时候是相隔很远的。依据根因和成果所在的代码区域不同,研究者将谬误分为了4类:safe -> safe、safe -> unsafe、unsafe -> safe、unsafe -> unsafe。比方:如果编码谬误呈现在safe代码中,但造成的成果体现在unsafe代码中,那么就归类为safe -> unsafe。 另一方面,依照传统的内存问题分类,问题又能够分为空间内存平安(Wrong Access)和工夫内存平安(Lifetime Violation)两大类,进一步可细分为缓冲区溢出(Buffer overflow)、解援用空指针(Null pointer dereferencing)、拜访未初始化内存(Reading uninitialized memory)、谬误开释(Invalid free)、开释后应用(Use after free)、反复开释(Double free)等几个小类。依据这两种分类维度,问题的统计数据如下: 从统计后果中能够看出,齐全不波及unsafe代码的内存平安问题只有一个。进一步考察发现这个问题呈现在Rust晚期的v0.3版本中,之后的稳固版本编译器曾经能拦挡这个问题。因而能够说:Rust语言的safe代码机制能十分无效的防止内存平安问题,所有稳固版本中发现的内存平安问题都和unsafe代码无关。 然而,这并不意味着咱们只有查看所有unsafe代码段就能无效发现问题。因为有时候问题根因会呈现在safe代码中,只是成果产生在unsafe代码段。论文中举了一个例子:(hi3ms没有Rust代码编辑性能,只能拿其余语言对付下了) Css 代码 pub fn sign(data: Option<&[u8]>) { let p = match data { Some(data) => BioSlice::new(data).as_ptr(), None => ptr::null_mut(), }; unsafe { let cms = cvt_p(CMS_sign(p)); } }在这段代码中,p是raw pointer类型,在safe代码中,当data含有值(Some分支)时,分支里试图创立一个BioSlice对象,并将对象指针赋给p。然而,依据Rust的生命周期规定,新创建的BioSlice对象在match表达式完结时就被开释了,p在传给CMS_sign函数时是一个野指针。这个例子中的unsafe代码段没有任何问题,如果只检视unsafe代码,不可能发现这个开释后应用的谬误。对此问题批改后的代码如下: Css 代码 pub fn sign(data: Option<&[u8]>) { let bio = match data { Some(data) => Some(BioSlice::new(data)), None => None, }; let p = bio.map_or(ptr::null_mut(),|p| p.as_ptr()); unsafe { let cms = cvt_p(CMS_sign(p)); } }批改后的代码正确的缩短了bio的生命周期。所有的批改都只产生在safe代码段,没有改变unsafe代码。 ...

August 13, 2020 · 2 min · jiezi

关于rust:rust学习生命周期

说生命周期,最经典的莫属空指针问题了: fn main() { { let r; { let x = 5; r = &x; } println!("r: {}", r); }}能够看到x的作用域在打印之前就曾经不起作用了,然而r对其进行了一个援用,所以在打印出仍旧须要应用x,因而就造成了空指针问题。解决办法就是转移将x的所属权,比方:r = x; 吐槽一下所有权,所有权并不是说变量被所有了,而是变量所指向的值被变量独自所领有,被别的变量借走了就变成了别的了,就不再领有所有权了。来看一个实例,比方咱们比拟两个字符串那个长度更短暂返回那个,咱们可能这样编写程序: fn main() { let string1 = String::from("abcd"); let string2 = "xyz"; let result = longest(string1.as_str(), string2); println!("The longest string is {}", result);}fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y }}看似没有问题,然而当咱们运行的时候会发现: D:\learn\cargo_learn>cargo run Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)error[E0106]: missing lifetime specifier --> src\main.rs:9:33 |9 | fn longest(x: &str, y: &str) -> &str { | ---- ---- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`help: consider introducing a named lifetime parameter |9 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { | ^^^^ ^^^^^^^ ^^^^^^^ ^^^error: aborting due to previous errorFor more information about this error, try `rustc --explain E0106`.error: could not compile `cargo_learn`.To learn more, run the command again with --verbose.程序报错了。错误信息竟然是编译的时候查看到返回的值要有所有权也就是要返回一个在生命周期中的值的变量。就像之前的空指针问题一样,所以咱们须要在返回和入参的时候思考到是否能够把生命周期也就是所有权也传入和返回呢,当然能够,谬误提醒中曾经给到了解决办法。 ...

August 13, 2020 · 4 min · jiezi

关于rust:rust学习泛型generics-Data-Types

咱们能够应用泛型为诸如函数签名或构造之类的项创立定义,而后能够将其用于许多不同的具体数据类型。 首先让咱们看一下如何应用泛型定义函数,构造,枚举和办法。 而后,咱们将探讨泛型如何影响代码性能。 在函数定义中举个例子,如果咱们定义两个函数,别离是求最大值和最大字符串且要求传入的参数都是一个数组,咱们可能这样去实现: fn largest_i32(list: &[i32]) -> i32 { let mut largest = list[0]; for &item in list { if item > largest { largest = item; } } largest}fn largest_char(list: &[char]) -> char { let mut largest = list[0]; for &item in list { if item > largest { largest = item; } } largest}fn main() { let number_list = vec![34, 50, 25, 100, 65]; let result = largest_i32(&number_list); println!("最大数是:{}", result); //100 let char_list = vec!['y', 'm', 'a', 'q']; let result = largest_char(&char_list); println!("最大字符是:{}", result); //y}通过上例咱们能够看到尽管两个函数办法传入的参数类型不统一,然而其行为是一样或者相似的。所以咱们是不是提取一个公共函数类进去,若能提取,此办法就能够称之为泛型:fn largest<T>(list: &[T]) -> T { ...

August 12, 2020 · 3 min · jiezi

关于rust:rust学习泛型generics-Data-Types

咱们能够应用泛型为诸如函数签名或构造之类的项创立定义,而后能够将其用于许多不同的具体数据类型。 首先让咱们看一下如何应用泛型定义函数,构造,枚举和办法。 而后,咱们将探讨泛型如何影响代码性能。 在函数定义中举个例子,如果咱们定义两个函数,别离是求最大值和最大字符串且要求传入的参数都是一个数组,咱们可能这样去实现: fn largest_i32(list: &[i32]) -> i32 { let mut largest = list[0]; for &item in list { if item > largest { largest = item; } } largest}fn largest_char(list: &[char]) -> char { let mut largest = list[0]; for &item in list { if item > largest { largest = item; } } largest}fn main() { let number_list = vec![34, 50, 25, 100, 65]; let result = largest_i32(&number_list); println!("最大数是:{}", result); //100 let char_list = vec!['y', 'm', 'a', 'q']; let result = largest_char(&char_list); println!("最大字符是:{}", result); //y}通过上例咱们能够看到尽管两个函数办法传入的参数类型不统一,然而其行为是一样或者相似的。所以咱们是不是提取一个公共函数类进去,若能提取,此办法就能够称之为泛型:fn largest<T>(list: &[T]) -> T { ...

August 12, 2020 · 3 min · jiezi

关于rust:rust学习泛型generics-Data-Types

咱们能够应用泛型为诸如函数签名或构造之类的项创立定义,而后能够将其用于许多不同的具体数据类型。 首先让咱们看一下如何应用泛型定义函数,构造,枚举和办法。 而后,咱们将探讨泛型如何影响代码性能。 在函数定义中举个例子,如果咱们定义两个函数,别离是求最大值和最大字符串且要求传入的参数都是一个数组,咱们可能这样去实现: fn largest_i32(list: &[i32]) -> i32 { let mut largest = list[0]; for &item in list { if item > largest { largest = item; } } largest}fn largest_char(list: &[char]) -> char { let mut largest = list[0]; for &item in list { if item > largest { largest = item; } } largest}fn main() { let number_list = vec![34, 50, 25, 100, 65]; let result = largest_i32(&number_list); println!("最大数是:{}", result); //100 let char_list = vec!['y', 'm', 'a', 'q']; let result = largest_char(&char_list); println!("最大字符是:{}", result); //y}通过上例咱们能够看到尽管两个函数办法传入的参数类型不统一,然而其行为是一样或者相似的。所以咱们是不是提取一个公共函数类进去,若能提取,此办法就能够称之为泛型:fn largest<T>(list: &[T]) -> T { ...

August 12, 2020 · 3 min · jiezi

关于rust:rust学习错误处理

Rust将谬误分为两大类:可复原谬误(recoverable)和不可复原谬误(unrecoverable)。 对于可复原的谬误,例如找不到文件谬误,能够正当地向用户报告问题,而后重试该操作。不可复原的谬误始终是谬误的症状,例如试图拜访数组开端以外的地位。 不可复原的谬误与panic!有时,咱们的代码中会产生好事,而您对此无能为力。 在这些状况下,Rust会执行panic!宏命令。 当panic!宏执行后,程序将打印一条失败音讯,开展并清理堆栈,而后退出。最常见的状况是在检测到某种谬误并且程序员不分明如何解决该谬误时产生。 让咱们在一个简略的程序中尝试panic!: fn main() { panic!("解体退出了");}D:\learn\cargo_learn>cargo run Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn) Finished dev [unoptimized + debuginfo] target(s) in 0.53s Running `target\debug\cargo_learn.exe`thread 'main' panicked at '解体退出了', src\main.rs:2:5note: run with `RUST_BACKTRACE=1` environment variable to display a backtraceerror: process didn't exit successfully: `target\debug\cargo_learn.exe` (exit code: 101)panic!宏命令与js中的throw Error属于同一性质,所以绝对于nodejs而言是同一个概念 应用panic!回溯让咱们借助于一个例子来看看啥时候panin!宏命令会被调用,调用不是咱们的代码被动调用的是来自库的调用,当咱们的代码存在谬误的时候。 fn main() { let v = vec![1, 2, 3]; v[99];}D:\learn\cargo_learn>cargo run Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn) Finished dev [unoptimized + debuginfo] target(s) in 0.55s Running `target\debug\cargo_learn.exe`thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', src\main.rs:4:5note: run with `RUST_BACKTRACE=1` environment variable to display a backtraceerror: process didn't exit successfully: `target\debug\cargo_learn.exe` (exit code: 101)在这里,咱们尝试拜访向量的第100个元素(索引从零开始,因而它位于索引99),然而它只有3个元素。 在这种状况下,Rust会panic。应该应用[]返回一个元素,然而如果传递有效索引,则Rust不会在此处返回正确的元素。 ...

August 12, 2020 · 5 min · jiezi

关于rust:rust学习在哈希map中存储具有关联值的键

与向量和Strings一样,哈希map也是也是一种常见的汇合。HashMap<K,V>类型存储K类型的键到V类型的值的映射。它通过散列函数来实现此操作,该函数确定如何将这些键和值搁置到内存中。 创立一个哈希map: //创立一个空哈希map,并应用insert办法进行插值fn main() { use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50); println!("{:?}", scores);}D:\learn\cargo_learn>cargo run Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn) Finished dev [unoptimized + debuginfo] target(s) in 0.79s Running `target\debug\cargo_learn.exe`{"Blue": 10, "Yellow": 50}请留神,咱们首先须要应用规范库的collections局部中的HashMap。 在咱们的三个常见汇合中(vec, STring),该汇合是最不罕用的汇合,因而未蕴含在尾声中主动纳入范畴的性能中。 哈企图也没有失去规范库的反对。 例如,没有内置的宏能够构建它们。 特地哈希map的key与value都别离是一个vec(向量),所以务必保障key向量的类型统一,value向量的类型统一; 结构哈企图的另一种办法是在元组的向量上应用迭代器和collect办法,其中每个元组都由一个键及其值组成。 collect办法将数据收集到多种收集类型中,包含HashMap。 例如,如果咱们在两个独自的向量中具备名称和初始值,则能够应用zip办法创立一个元组向量,其中"Blue"与10配对,依此类推。 而后,咱们能够应用collect办法将元组的矢量转换为哈企图,如下例所示: fn main() { use std::collections::HashMap; let teams = vec![String::from("Blue"), String::from("Yellow")]; let initial_scores = vec![10, 50]; let mut scores: HashMap<_, _> = teams.into_iter().zip(initial_scores.into_iter()).collect(); println!("{:#?}", scores)}D:\learn\cargo_learn>cargo run Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)warning: variable does not need to be mutable --> src\main.rs:7:9 |7 | let mut scores: HashMap<_, _> = | ----^^^^^^ | | | help: remove this `mut` | = note: `#[warn(unused_mut)]` on by defaultwarning: 1 warning emitted Finished dev [unoptimized + debuginfo] target(s) in 0.82s Running `target\debug\cargo_learn.exe`{ "Yellow": 50, "Blue": 10,}这里须要应用类型正文HashMap <_,_>,因为它能够收集到许多不同的数据结构中,并且Rust除非您指定,否则都不晓得您想要哪个。 然而,对于键和值类型的参数,咱们应用下划线,Rust能够依据向量中数据的类型推断哈希映射蕴含的类型。 在上例中,键类型将为String,而值类型将为i32。 ...

August 12, 2020 · 2 min · jiezi

关于rust:rust学习字符串Strings

什么是字符串(String)? Rust在外围语言中只有一种字符串类型,即字符串切片str,通常以借用模式&str看到。 String类型是Rust的规范库提供的,而不是编码为外围语言,它是一种可增长、可变、可领有的以UTF-8模式编码的字符串类型。 在Rust中援用"strings"时,它们通常指的是String和字符串切片&str类型,但strings不仅仅是这两种类型。但两种类型在Rust的规范库中应用最多,并且String和字符串切片&str都是UTF-8编码的。 Rust的规范库还包含许多其余字符串类型,例如OsString,OsStr,CString和CStr。 图书馆包装箱(Library crates)能够提供更多用于存储字符串数据的选项。为啥这些名称如何都以String或Str结尾? 因为它们指的是领有和借用的变体,就像String和str类型一样。例如,这些字符串类型能够用不同的编码存储文本,或以不同的形式在内存中示意。创立一个新的字符串 fn main() { let mut s = String::new(); //创立一个新的空字符串名为s,之后就能够应用s。}通常,咱们会应用一些初始数据作为字符串的结尾。 为此,咱们应用to_string办法,该办法可在实现Display特色的任何类型上应用,就像字符串文字一样。如下所示两个成果完全相同的示例: //此代码创立一个蕴含初始内容的字符串。fn main() { let data = "初始内容"; let s = data.to_string(); // 该办法也能够间接解决文字: let s = "初始内容".to_string();} 咱们还能够应用函数String::from从字符串文字创立字符串。成果等同于应用to_string: fn main() { let s = String::from("初始内容");}认为字符串是UTF-8编码的,所以咱们能够在其中蕴含任何正确编码的数据(不论它长啥样),如下所示: fn main() { let hello = String::from("你好"); let hello = String::from("Hello"); let hello = String::from(" "); let hello = String::from("Dobrý den"); let hello = String::from(""); let hello = String::from(""); let hello = String::from("こんにちは"); let hello = String::from("안녕하세요"); let hello = String::from("Olá"); let hello = String::from(""); let hello = String::from("Hola");}更新字符串 ...

August 12, 2020 · 3 min · jiezi

关于rust:rust学习用向量vectors存储值列表

向量使咱们能够在单个数据结构中存储多个值,该构造将所有值彼此相邻搁置在内存中。 向量只能存储雷同类型的值。当咱们具备我的项目列表时,例如文件中的文本行或购物车中我的项目的价格时,它们很有用。 新建一个vector fn main() { let v: Vec<i32> = Vec::new(); println!("{:?}", v)}D:\learn\cargo_learn>cargo run Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn) Finished dev [unoptimized + debuginfo] target(s) in 1.06s Running `target\debug\cargo_learn.exe`[]能够看到其实vector就是一个数组,只不过有一个要求就是数组中的每一个元素必须具备雷同的类型,上面创立一个实在的实例: fn main() { let v = vec![1, 2, 3]; println!("{:?}", v);}D:\learn\cargo_learn>cargo run Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn) Finished dev [unoptimized + debuginfo] target(s) in 0.52s Running `target\debug\cargo_learn.exe`[1, 2, 3]如果不必雷同类型的数据会怎么呢? fn main() { let v = vec![1, 2.1, 3]; println!("{:?}", v);}D:\learn\cargo_learn>cargo run Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)error[E0308]: mismatched types --> src\main.rs:2:21 |2 | let v = vec![1, 2.1, 3]; | ^^^ expected integer, found floating-point numbererror: aborting due to previous errorFor more information about this error, try `rustc --explain E0308`.error: could not compile `cargo_learn`.To learn more, run the command again with --verbose.更新一个向量在rust中不论更新任何变量,其必须是可变的 ...

August 11, 2020 · 2 min · jiezi

关于rust:rust学习将模块分成不同的文件

随着程序的越来越简单,咱们须要将单文件宰割成多文件。当初创立main.rs文件,其内容如下: mod front_of_house;pub use crate::front_of_house::hosting;pub fn eat_at_restaurant() { hosting::add_to_waitlist(); hosting::add_to_waitlist(); hosting::add_to_waitlist();}fn main(){ eat_at_restaurant()}创立front_of_house.rs文件,其内容如下: pub mod hosting;创立文件夹src/front_of_house并新增加一个文件hosting.rs,其内容如下: #![allow(unused_variables)]pub fn add_to_waitlist() { println!("被调用了!!!")}运行一下: D:\learn\cargo_learn>cargo run Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn) Finished dev [unoptimized + debuginfo] target(s) in 0.47s Running `target\debug\cargo_learn.exe`被调用了!!!被调用了!!!被调用了!!!D:\learn\cargo_learn>留神:若想调用一个内部文件裸露进去的模块并将其裸露进来,那么就必须新建一个跟本文件同名称的文件夹,并将内部文件放入此文件夹中,最好模块拆分的很洁净能和文件名称保持一致最好

August 11, 2020 · 1 min · jiezi

关于rust:rust学习引用模块树中项目的路径Paths

为了向Rust展现在模块树中找到我的项目的地位,咱们应用的门路与浏览文件系统时应用的门路雷同。 如果要调用函数,则须要晓得其门路。 门路能够采纳两种模式:绝对路径从板条箱根开始通过应用一个create名称或一个文字create。相对路径从以后模块开始,并在以后模块中应用self,super或标识符。绝对路径和相对路径后跟一个或多个标识符,并用双冒号分隔(::)例如: mod front_of_house { mod hosting { fn add_to_waitlist() {} }}pub fn eat_at_restaurant() { // 绝对路径 crate::front_of_house::hosting::add_to_waitlist(); // 相对路径 front_of_house::hosting::add_to_waitlist();}运行cargo build会报错: D:\learn\cargo_learn>cargo build Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)error[E0603]: module `hosting` is private --> src\lib.rs:9:28 |9 | crate::front_of_house::hosting::add_to_waitlist(); | ^^^^^^^ private module |note: the module `hosting` is defined here --> src\lib.rs:2:5 |2 | mod hosting { | ^^^^^^^^^^^error[E0603]: module `hosting` is private --> src\lib.rs:12:21 |12 | front_of_house::hosting::add_to_waitlist(); | ^^^^^^^ private module |note: the module `hosting` is defined here --> src\lib.rs:2:5 |2 | mod hosting { | ^^^^^^^^^^^error: aborting due to 2 previous errorsFor more information about this error, try `rustc --explain E0603`.error: could not compile `cargo_learn`.To learn more, run the command again with --verbose.谬误音讯表明模块托管是公有的。换句话说,咱们领有托管模块和add_to_waitlist函数的正确门路,但Rust无法访问它们,因为它无法访问公有局部。 ...

August 11, 2020 · 2 min · jiezi

关于rust:rust学习定义模块modules用以控制范围和隐私

模块使咱们能够将板条箱中的代码分为几组,以进步可读性和重用性。 模块还管制我的项目的隐衷,即我的项目是能够由内部代码应用(公共)还是外部实现细节而不能用于内部应用(公有)。 举例来说,让咱们写一个提供餐厅性能的图书馆箱子。 咱们将定义性能的签名,但将其主体留空以专一于代码的组织,而不是理论在代码中实现餐厅。 在餐饮业中,餐厅的某些局部称为屋宇前部,而其余局部称为屋宇后部。 屋前就是顾客的所在。 主机在这里款待客户,服务器在承受订单和付款,调酒师在这里喝酒。 屋后是厨师在厨房工作,洗碗碟机,经理进行行政工作的中央。 首先来创立一个名为lib.rs的文件(能够通过命令:cargo new --lib restaurant来创立),并编辑内容: mod front_of_house { mod hosting { fn add_to_waitlist() {} fn seat_at_table() {} } mod serving { fn take_order() {} fn serve_order() {} fn take_payment() {} }}fn main() {}咱们以mod关键字结尾定义模块,而后指定模块名称(在本例中为front_of_house),并在模块主体四周搁置花括号。 在模块外部,咱们能够有其余模块,在这种状况下,这些模块能够托管和服务。 模块还能够保留其余我的项目的定义,例如构造,枚举,常量,特色或者办法。 通过应用模块,咱们能够将相干的定义分组在一起,并命名它们为什么相干。 应用此代码的程序员能够更轻松地找到他们想要应用的定义,因为他们能够基于组导航代码,而不用浏览所有定义。 向此代码增加新性能的程序员将晓得将代码搁置在何处以放弃程序的组织性。

August 11, 2020 · 1 min · jiezi

关于rust:rust学习PackagesCratesModules-usePaths

Packages: 一个Cargo个性,能够使咱们构建测试和共享CratesCrates: 生成库或可执行文件的模块树Modules 和 use:让咱们管制门路的组织,范畴和隐衷Paths: 命名我的项目的一种办法,例如构造,函数或模块

August 11, 2020 · 1 min · jiezi

关于rust:rust学习使用-if-let-实现简单的控制流

if let语法使您能够将if和let组合成一种不太简短的形式来解决与一种模式匹配的值,而疏忽其余模式。 例如:该程序与Option <u8>值匹配,但仅想在值为3时执行代码: fn main() { let some_u8_value = Some(0u8); match some_u8_value { Some(3) => println!("three"), _ => (println!("啥也不是")), }}咱们想对Some(3)匹配做点什么,但对其余Some <u8>值或None值什么都不做。 为了满足match表达式,咱们只须要解决一个变体就增加_ =>(),然而须要增加的许多样板代码。相同,咱们能够应用if let以更短的形式编写此代码并实现完全一致的匹配成果: fn main() { let some_u8_value = Some(8u8); if let Some(3) = some_u8_value { println!("three"); } if let Some(8) = some_u8_value { println!("匹配到了"); }}D:\learn\cargo_learn>cargo run Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn) Finished dev [unoptimized + debuginfo] target(s) in 0.27s Running `target\debug\cargo_learn.exe`匹配到了相应的如果咱们想要失去一个确切的后果,比方如果没有匹配到咱们须要的那个咱们心愿有返回后果,那么此时能够应用else: #[derive(Debug)]enum UsState { Alabama, Alaska,}enum Coin { Penny, Quarter(UsState),}fn main() { let coin = Coin::Penny; let mut count = 0; if let Coin::Quarter(state) = coin { println!("State quarter from {:?}!", state); } else { count += 1; println!("没有匹配到任何后果, count将主动+1 !!!") }}D:\learn\cargo_learn>cargo run Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)warning: variable `count` is assigned to, but never used --> src\main.rs:14:9 |14 | let mut count = 0; | ^^^^^^^^^ | = note: `#[warn(unused_variables)]` on by default = note: consider using `_count` insteadwarning: value assigned to `count` is never read --> src\main.rs:18:9 |18 | count += 1; | ^^^^^ | = note: `#[warn(unused_assignments)]` on by default = help: maybe it is overwritten before being read?warning: variant is never constructed: `Alabama` --> src\main.rs:3:5 |3 | Alabama, | ^^^^^^^ | = note: `#[warn(dead_code)]` on by defaultwarning: variant is never constructed: `Alaska` --> src\main.rs:4:5 |4 | Alaska, | ^^^^^^warning: variant is never constructed: `Quarter` --> src\main.rs:9:5 |9 | Quarter(UsState), | ^^^^^^^^^^^^^^^^warning: 5 warnings emitted Finished dev [unoptimized + debuginfo] target(s) in 0.51s Running `target\debug\cargo_learn.exe`没有匹配到任何后果, count将主动+1 !!!

August 11, 2020 · 2 min · jiezi

关于rust:rust学习匹配控制流运算符match

Rust具备一个十分弱小的控制流运算符,称为match,它使咱们能够将值与一系列模式进行比拟,而后依据匹配的模式执行代码。 模式能够由文字值,变量名,通配符和许多其余内容组成。个人感觉跟js外面的switch一样~^~ 介绍该点的rust的教程给了一个十分有意思的比喻:能够将匹配表达式想像成硬币分类机:硬币沿着轨道滑动,轨道上有各种大小的孔,每枚硬币都从遇到的第一个孔中掉落。 以雷同的形式,值在匹配中遍历每个模式,并且在第一个模式中值“适宜”,该值落入要在执行期间应用的关联代码块。 match用法样例: use crate::Coin::Penny;enum Coin { Penny, Nickel, Dime, Quarter,}fn value_in_cents(coin: Coin) -> u8 { match coin { Coin::Penny => 1, Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter => 25, }}fn main() { println!("{}", value_in_cents(Penny))}D:\learn\cargo_learn>cargo run Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)warning: variant is never constructed: `Nickel` --> src\main.rs:5:5 |5 | Nickel, | ^^^^^^ | = note: `#[warn(dead_code)]` on by defaultwarning: variant is never constructed: `Dime` --> src\main.rs:6:5 |6 | Dime, | ^^^^warning: variant is never constructed: `Quarter` --> src\main.rs:7:5 |7 | Quarter, | ^^^^^^^warning: 3 warnings emitted Finished dev [unoptimized + debuginfo] target(s) in 1.03s Running `target\debug\cargo_learn.exe`1目前为止除了写法不统一之外,跟js外面的switch还没有任何区别绑定到值的模式match的另一个有用性能是它们能够绑定到与模式匹配的局部值。 这就是咱们能够从枚举变量中提取值的形式。 ...

August 11, 2020 · 3 min · jiezi

关于rust:rust学习方法语法Method-Syntax

构想一个场景,咱们定义了很多的构造,然而之前都只是为了贮存一些静态数据,那能不能通过增加一些属性,增加的属性值是一个办法,该办法用于返回一个跟构造自身参数相关联的计算结果呢? 是能够的,废话少说先上示例: #[derive(Debug)]struct Rectangle { width: u32, height: u32,}impl Rectangle { fn get_perimeter(&self) -> u32 { (self.width + self.height) * 2 }}fn main() { let rect1 = Rectangle { width: 36, height: 18, }; println!("矩形:{:?}的矩形的周长为:{}", rect1, rect1.get_perimeter())}cargo run 矩形:Rectangle { width: 36, height: 18 }的矩形的周长为:108为了在Rectangle的上下文中定义函数,咱们启动一个impl(实现)块。而后,咱们将区域函数挪动到impl大括号内,并将第一个(在这种状况下,仅此参数)参数更改为签名中和注释中各处的self。在main中,咱们调用了get_perimeter函数并传递了rect1作为参数,咱们能够改用办法语法在Rectangle实例上调用get_perimeter办法。办法语法在实例之后:咱们在办法名称,括号和任何参数前面增加一个点。 在区域签名中,咱们应用&self而不是矩形:&Rectangle,因为Rust晓得self的类型为Rectangle,因为该办法位于impl Rectangle上下文中。请留神,就像在&Rectangle中一样,咱们仍须要在self之前应用&。办法能够获得自我的所有权,像咱们在这里所做的那样,不可变地借用自我,或者其自身是可变的去借用自我,就像它们能够应用任何其余参数一样。 咱们在此处抉择&self的起因与在函数版本中应用&Rectangle的起因雷同:咱们不想取得所有权,咱们只想读取构造中的数据,而不是对其进行写入。如果要在办法执行的过程中更改调用办法的实例,则能够应用&mut self作为第一个参数。很少有一种办法只应用self作为第一个参数来获取实例的所有权;该办法通常在办法将本身转换为其余模式并且要避免调用者在转换后应用原始实例时应用。 除了应用办法语法而且不用在每个办法的签名中重复使用self的类型之外,应用办法代替函数的次要益处是对程序结构而言。咱们将类型实例的所有性能都放在一个impl块中,而不是让咱们的代码的将来用户在咱们提供的库中的各个地位搜寻Rectangle的性能。 一个struct能够有多个impl有很多参数状况的办法 让咱们通过在Rectangle构造上实现第二种办法来练习应用办法。 这次,咱们心愿Rectangle的一个实例承受Rectangle的另一个实例,如果第二个Rectangle能够齐全适宜本身(宽度是长度的2倍),则返回true。 否则应返回false。 也就是说,一旦定义了can_hold办法,咱们心愿可能编写上面示例所代表的程序: #[derive(Debug)]struct Rectangle { width: u32, height: u32,}impl Rectangle { fn get_perimeter(&self) -> u32 { (self.width + self.height) * 2 }}impl Rectangle { fn can_hold(&self, other: &Rectangle) -> bool { self.width > other.width && self.height > other.height }}fn main() { let rect1 = Rectangle { width: 36, height: 18, }; let rect2 = Rectangle { width: 100, height: 18, }; let rect3 = Rectangle { width: 12, height: 6, }; println!("矩形rect1:{:?}能够包容矩形rect2:{:?}吗? {}", rect1, rect2, rect1.can_hold(&rect2)); println!("矩形rect1:{:?}能够包容矩形rect3:{:?}吗? {}", rect1, rect3, rect1.can_hold(&rect3));}cargo run |8 | fn get_perimeter(&self) -> u32 { | ^^^^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by defaultwarning: 1 warning emitted Finished dev [unoptimized + debuginfo] target(s) in 0.53s Running `target\debug\cargo_learn.exe`矩形rect1:Rectangle { width: 36, height: 18 }能够包容矩形rect2:Rectangle { width: 100, height: 18 }吗? false矩形rect1:Rectangle { width: 36, height: 18 }能够包容矩形rect3:Rectangle { width: 12, height: 6 }吗? true相干性能 ...

August 11, 2020 · 2 min · jiezi

关于rust:rust学习使用structs初探

在rust中创立一个构造struct貌似很简略,也很容易获取到属性值。那么该怎么应用呢?是否波及到所有权呢?如果波及到又该如何解决呢? 首先通过一个求矩形周长的示例来逐渐深刻: fn main() { let width = 36; let height = 18; println!("宽为:{},高为:{}的矩形的周长为:{}", width, height, get_perimeter(width, height))}fn get_perimeter(width: u32, height: u32) -> u32 { (width + height) * 2}cargo run 宽为:36,高为:18的矩形的周长为:108用元组重构: 获取周长函数计算一个矩形的周长,然而咱们编写的函数有两个参数。而参数又是相关联的,然而在上述例子中并没有任何一处来表白。将宽度与高度组合在一起会不会更简略一点呢,比方方便管理与容易了解,无妨借助tuple试试: fn main() { let rect1 = (36,18); println!("宽高:{:?}的矩形的周长为:{}", rect1, get_perimeter(rect1))}fn get_perimeter(dimensions: (u32, u32)) -> u32 { (dimensions.0 + dimensions.1) * 2}cargo run 宽高:(36, 18)的矩形的周长为:108在某种程度上,该办法比最开始的要好。 元组让咱们减少了一些构造,当初咱们只传递一个参数。 但以最开始的形式,这个办法有有些不清晰:元组没有命名其元素,导致咱们的计算变得更加凌乱,因为咱们必须索引元组的各个局部。 咱们是否要混合宽度和高度以进行面积计算并不重要,然而如果咱们想在屏幕上绘制矩形,那就很重要了! 咱们必须得记住,width是元组索引0,height是元组索引1。如果其他人在编写此代码,则他们必须弄清楚这一点并牢记在心。 容易遗记或混同这些值并导致谬误,因为咱们没有在代码中传播数据的含意。 用构造重构:增加更多含意 咱们应用构造通过标记数据来增加含意。 咱们能够将正在应用的元组转换为具备整体名称和局部名称的数据类型,如下示例: #[derive(Debug)]struct Rectangle { width: u32, height: u32,}fn main() { let rect1 = Rectangle { width: 36, height: 18, }; println!("矩形:{:?}的矩形的周长为:{}", rect1, get_perimeter(&rect1))}fn get_perimeter(rectangle: &Rectangle) -> u32 { (rectangle.width + rectangle.height) * 2}cargo run 矩形:Rectangle { width: 36, height: 18 }的矩形的周长为:108通过上例能够分明地看到咱们的程序变得清晰简洁且便于了解了,此处因为只是借用一下矩形的宽高的数据并不对其进行更改,所以应用的时候必须借助&来实现,在此处设计到了所有权的问题,如果没有借用&的话,会导致空指针的问题,如下所示: ...

August 11, 2020 · 1 min · jiezi

关于rust:rust学习Structs

Structs相似于ts中的Interface,权且称之为构造吧。在rust中它跟tuple十分类似。与元组不同的是,咱们能够为每个数据命名,以便分明地晓得这些值的含意。 因为应用了这些名称,因而构造比元组更灵便:不用依赖数据的程序来指定或拜访实例的值。 废话少说,先来一个structs的实例: struct User { username: String, email: String, sign_in_count: u64, active: bool,}要定义一个构造,咱们输出关键字struct并命名整个构造。 构造的名称应阐明将数据分组在一起的重要性。 而后,在大括号内,定义数据片段的名称和类型,咱们将其称为字段。 struct的应用,十分相似于interface,比方应用下面定义的User,相似于ts中定于一个特定的对象: #[derive(Debug)]struct User { username: String, email: String, sign_in_count: u64, active: bool,}fn main() { let user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; println!("{:?}", user1)}cargo run User { username: "someusername123", email: "someone@example.com", sign_in_count: 1, active: true }留神想要把构造实例打印进去必须在应用struct的下面增加一行#[derive(Debug)],否则就会报错。那么,如何应用和扭转一个构造实例中的属性呢?看实例: println!("{:?}", user1.email);//如果定义的实例是一个可变变量的话,那么能够扭转其属性值。//let mut user1 = User {...}user1.email = String::from("test@example.com");println!("{:?}", user1.email);cargo run "someone@example.com" "test@example.com"是否能够向js中的工厂办法来获取实例呢,能够的,例如: ...

August 10, 2020 · 2 min · jiezi

关于rust:rust学习Structs

Structs相似于ts中的Interface,权且称之为构造吧。在rust中它跟tuple十分类似。与元组不同的是,咱们能够为每个数据命名,以便分明地晓得这些值的含意。 因为应用了这些名称,因而构造比元组更灵便:不用依赖数据的程序来指定或拜访实例的值。 废话少说,先来一个structs的实例: struct User { username: String, email: String, sign_in_count: u64, active: bool,}要定义一个构造,咱们输出关键字struct并命名整个构造。 构造的名称应阐明将数据分组在一起的重要性。 而后,在大括号内,定义数据片段的名称和类型,咱们将其称为字段。 struct的应用,十分相似于interface,比方应用下面定义的User,相似于ts中定于一个特定的对象: #[derive(Debug)]struct User { username: String, email: String, sign_in_count: u64, active: bool,}fn main() { let user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; println!("{:?}", user1)}cargo run User { username: "someusername123", email: "someone@example.com", sign_in_count: 1, active: true }留神想要把构造实例打印进去必须在应用struct的下面增加一行#[derive(Debug)],否则就会报错。那么,如何应用和扭转一个构造实例中的属性呢?看实例: println!("{:?}", user1.email);//如果定义的实例是一个可变变量的话,那么能够扭转其属性值。//let mut user1 = User {...}user1.email = String::from("test@example.com");println!("{:?}", user1.email);cargo run "someone@example.com" "test@example.com"是否能够向js中的工厂办法来获取实例呢,能够的,例如: ...

August 10, 2020 · 2 min · jiezi

关于rust:rust学习所属权之Slice

还有一种不领有所属权的数据类型为:slice。slice使咱们能够援用汇合中元素的间断序列而不是整个汇合。 一个小的编程问题:编写一个函数,该函数须要一个字符串并返回在该字符串中找到的第一个单词。 如果函数在字符串中找不到空格,则整个字符串必须是一个单词,因而应返回整个字符串。 fn first_word(s: &String) -> usize { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return i; } } s.len()}在上例中:1、因为咱们须要一一查看String元素,并查看值是否为空格,所以咱们将应用as_bytes办法将String转换为字节数组:let bytes = s.as_bytes();2、咱们应用iter办法在字节数组上创立一个迭代器:for (i, &item) in bytes.iter().enumerate() { 3、在for循环内,咱们应用字节文字语法搜寻示意空格的字节。 如果找到空格,则返回地位。 否则,咱们应用s.len()返回字符串的长度: if item == b' ' { return i; } } s.len()4、当初,咱们能够找到字符串中第一个单词结尾的索引,但这是有问题的, 咱们将本人返回一个usize,然而在&String的上下文中,这只是一个有意义的数字。 换句话说,因为它是与String离开的值,因而无奈保障它在未来依然无效,所以将其革除: fn main() { let mut s = String::from("hello world"); let word = first_word(&s); // word将要获取到的值为5 s.clear(); // 这将清空字符串,使其等于"" // word依然在这里具备值5,然而没有更多的字符串可用于有意义地应用值5.这个词当初齐全有效! }该程序编译时没有任何谬误,如果在调用s.clear()之后应用word,也能够这样做。 因为word基本没有连贯到s的状态,所以word依然蕴含值5。咱们能够将值5与变量s一起应用以尝试提取第一个单词,但这将是一个bug,因为自从咱们保留了5到word中之后,s产生了变动。 ...

August 10, 2020 · 2 min · jiezi

关于rust:rust学习所属权之Slice

还有一种不领有所属权的数据类型为:slice。slice使咱们能够援用汇合中元素的间断序列而不是整个汇合。 一个小的编程问题:编写一个函数,该函数须要一个字符串并返回在该字符串中找到的第一个单词。 如果函数在字符串中找不到空格,则整个字符串必须是一个单词,因而应返回整个字符串。 fn first_word(s: &String) -> usize { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return i; } } s.len()}在上例中:1、因为咱们须要一一查看String元素,并查看值是否为空格,所以咱们将应用as_bytes办法将String转换为字节数组:let bytes = s.as_bytes();2、咱们应用iter办法在字节数组上创立一个迭代器:for (i, &item) in bytes.iter().enumerate() { 3、在for循环内,咱们应用字节文字语法搜寻示意空格的字节。 如果找到空格,则返回地位。 否则,咱们应用s.len()返回字符串的长度: if item == b' ' { return i; } } s.len()4、当初,咱们能够找到字符串中第一个单词结尾的索引,但这是有问题的, 咱们将本人返回一个usize,然而在&String的上下文中,这只是一个有意义的数字。 换句话说,因为它是与String离开的值,因而无奈保障它在未来依然无效,所以将其革除: fn main() { let mut s = String::from("hello world"); let word = first_word(&s); // word将要获取到的值为5 s.clear(); // 这将清空字符串,使其等于"" // word依然在这里具备值5,然而没有更多的字符串可用于有意义地应用值5.这个词当初齐全有效! }该程序编译时没有任何谬误,如果在调用s.clear()之后应用word,也能够这样做。 因为word基本没有连贯到s的状态,所以word依然蕴含值5。咱们能够将值5与变量s一起应用以尝试提取第一个单词,但这将是一个bug,因为自从咱们保留了5到word中之后,s产生了变动。 ...

August 10, 2020 · 2 min · jiezi

关于rust:rust学习所有权之参考和借阅

应用元祖tuple接管函数的返回值,在所有权问题中会产生:咱们必须将入参返回给调用函数,略显繁冗。 rust中一种新的援用形式: fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); println!("The length of '{}' is {}.", s1, len);}fn calculate_length(s: &String) -> usize { s.len()}与应用tuple的形式相比,上例:1、变量申明和函数返回值中的所有元组代码都隐没了;2、将&s1传递给calculate_length,并且在其定义中,采纳&String而不是String。 这种形式(& 变量)被称为参考,它们使咱们能够援用某些值而无需领有其所有权。留神:应用&进行援用的反义词是解援用,这是通过解援用运算符*实现的。在上例中: let s1 = String::from("hello");let len = calculate_length(&s1);&s1语法使咱们能够创立一个援用s1的参考,但该援用不领有它。 因为不领有它,所以当援用超出作用域时,它所指向的值将不会被删除。同样,函数的签名应用&示意参数s的类型是援用。 其解释为: fn calculate_length(s: &String) -> usize { // s是对字符串的一个援用 s.len()} // 这里,变量s超出了作用域,然而因为其自身并不具备所有权(也就是它所指向的内容),所以什么也不会产生变量s无效的作用域与任何函数参数的作用域雷同,然而当它超出作用域时,因为没有所有权,rust不会删除援用指向的内容。 当函数应用援用作为参数而不是理论值作为参数时,rust将不须要返回这些值来偿还所有权,因为在此模块中rust从未领有过所有权。 rust中称援用(参考)为函数参数借用。 与现实生活中一样,如果某人领有某物,则能够向他们借用。 实现后,咱们必须将其偿还。 那么,借用的内容是否可被批改呢?是不能够的,例如: fn main() { let s = String::from("hello"); change(&s);}fn change(some_string: &String) { some_string.push_str(", world");}$ cargo run |7 | fn change(some_string: &String) { | ------- help: consider changing this to be a mutable reference: `&mut std::string::String`8 | some_string.push_str(", world"); | ^^^^^^^^^^^ `some_string` is a `&` reference, so the data it refers to cannot be borrowed as mutableerror: aborting due to previous errorFor more information about this error, try `rustc --explain E0596`.error: could not compile `ownership`.To learn more, run the command again with --verbose.正如变量在默认状况下是不可变的一样,参考也是如此。 rust不容许批改参考的内容。可变援用如果我肯定要批改某一个援用的值呢,先来看一个例子: ...

August 10, 2020 · 3 min · jiezi

关于rust:rust学习所有权之参考和借阅

应用元祖tuple接管函数的返回值,在所有权问题中会产生:咱们必须将入参返回给调用函数,略显繁冗。 rust中一种新的援用形式: fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); println!("The length of '{}' is {}.", s1, len);}fn calculate_length(s: &String) -> usize { s.len()}与应用tuple的形式相比,上例:1、变量申明和函数返回值中的所有元组代码都隐没了;2、将&s1传递给calculate_length,并且在其定义中,采纳&String而不是String。 这种形式(& 变量)被称为参考,它们使咱们能够援用某些值而无需领有其所有权。留神:应用&进行援用的反义词是解援用,这是通过解援用运算符*实现的。在上例中: let s1 = String::from("hello");let len = calculate_length(&s1);&s1语法使咱们能够创立一个援用s1的参考,但该援用不领有它。 因为不领有它,所以当援用超出作用域时,它所指向的值将不会被删除。同样,函数的签名应用&示意参数s的类型是援用。 其解释为: fn calculate_length(s: &String) -> usize { // s是对字符串的一个援用 s.len()} // 这里,变量s超出了作用域,然而因为其自身并不具备所有权(也就是它所指向的内容),所以什么也不会产生变量s无效的作用域与任何函数参数的作用域雷同,然而当它超出作用域时,因为没有所有权,rust不会删除援用指向的内容。 当函数应用援用作为参数而不是理论值作为参数时,rust将不须要返回这些值来偿还所有权,因为在此模块中rust从未领有过所有权。 rust中称援用(参考)为函数参数借用。 与现实生活中一样,如果某人领有某物,则能够向他们借用。 实现后,咱们必须将其偿还。 那么,借用的内容是否可被批改呢?是不能够的,例如: fn main() { let s = String::from("hello"); change(&s);}fn change(some_string: &String) { some_string.push_str(", world");}$ cargo run |7 | fn change(some_string: &String) { | ------- help: consider changing this to be a mutable reference: `&mut std::string::String`8 | some_string.push_str(", world"); | ^^^^^^^^^^^ `some_string` is a `&` reference, so the data it refers to cannot be borrowed as mutableerror: aborting due to previous errorFor more information about this error, try `rustc --explain E0596`.error: could not compile `ownership`.To learn more, run the command again with --verbose.正如变量在默认状况下是不可变的一样,参考也是如此。 rust不容许批改参考的内容。可变援用如果我肯定要批改某一个援用的值呢,先来看一个例子: ...

August 10, 2020 · 3 min · jiezi

关于rust:rust学习所属权基本概念

所属权是rust最有特色的性能,该性能使得rust无需垃圾回收机制即可保障内存平安。那么所属权是啥以及有啥用呢? 首先,rust中的所属权含意:1、所属权规定: (1)、Rust中的每个值都有一个变量,称为其所有者。(2)、一次只能是一个所有者。(3)、当所有者超出作用范畴时,该值将被删除。2、什么是作用范畴:作用范畴相似于:{let s = "hello";} 3、内存和调配: 在rust中,当领有内存的变量超出范围后,内存将主动开释,例如: fn main() { { // 从当初开始,s是无效的 let s = String::from("hello"); //用s做一些操作 } // 此作用域现已完结,并且s不再无效}**: 当变量超出范围时,Rust为咱们调用一个非凡函数。 此函数称为drop,它是String的创建者(以上例进行阐明)能够在其中搁置代码以返回内存的中央。 Rust会主动在右大括号敞开除调用。 *变量和数据交互的形式:挪动多个变量能够在Rust中以不同的形式与同一数据交互。例如: fn main() { let x = 5; let y = x;}//“将值5绑定到x; 而后在x中复制值并将其绑定到y。” 当初,咱们有两个变量x和y,它们都等于5。这种状况是确确实实存在的,因为整数是具备已知固定大小的简略值,并且这5个值被压入堆栈。再来看看String类型的: fn main() { let s1 = String::from("hello"); let s2 = s1;}//这看起来与先前的代码十分类似,因而咱们能够假如它的工作形式是雷同的:也就是说,第二行将在s1中复制该值并将其绑定到s2。 但这不是齐全会产生的事件。// 字符串由三局部组成:指向内存的指针,用于保留字符串的内容,长度和容量。 这组数据存储在堆栈中。 //长度是String以后正在应用的内存量(以字节为单位)。 容量是String从分配器接管的内存总量(以字节为单位)。 长度和容量之间的差别很重要,但在这种状况下并不重要,因而,临时能够疏忽容量。//当咱们将s1调配给s2时,将复制String数据,这意味着咱们将复制堆栈上的指针,长度和容量。 咱们不将数据复制到指针援用的堆上。//如果Rust代替复制了堆数据,那么如果堆上的数据很大,则运行时性能s2 = s1可能会十分低廉。//当变量超出范围时,Rust主动调用drop函数并革除该变量的堆内存。 然而如果存在两个指向雷同地位的数据指针。 这是一个问题:当s2和s1超出范围时,它们都将尝试开释雷同的内存。 这被称为双重开释谬误,是内存平安谬误之一。 两次开释内存可能导致内存损坏,从而可能导致安全漏洞。确保数据安全:为了确保内存平安,Rust中呈现了这种状况的详细信息。 Rust不会尝试复制调配的内存,而是认为s1不再无效,因而,当s1超出范围时,Rust不须要开释任何内容。 查看创立s2之后尝试应用s1会产生什么状况; 它不起作用: ...

August 10, 2020 · 2 min · jiezi

关于rust:rust学习控制流

rust对数据流的管制大抵体现在以下几点: 1、if条件判断;2、loop/while/for循环;1、if条件判断: rust的条件判断逻辑与js一样,惟一不同点是规范的判断语句能够不必写在()外面,例如: fn main() { let number = 6; if number % 4 == 0 { println!("numer是4的整数倍"); } else if number % 3 == 0 { println!("numer是3的整数倍"); } else if number % 2 == 0 { println!("numer是2的整数倍"); } else { println!("number不是2或3或4的整数倍"); } let condition = true; let number = if condition { 5 } else { 6 }; println!("number的值为: {}", number);}运行后果: number不是2或3或4的整数倍 number的值为: 5特地if条件为真与条件为假的时候如果是返回一个变量,那么返回的变量必须具备雷同的类型 2、loop循环: ...

August 10, 2020 · 1 min · jiezi

关于rust:rust学习方程functions

rust中所有的函数以fn关键字进行结尾,其中入口文件main.rs中的fn main(){}是主函数,也即不须要main()就主动默认运行的函数。 1、rust中的函数就像是应用var进行定义的函数一样,能够在应用之前就进行定义或者在之后进行定义,例如: fn main() { println!("Hello, world!"); another_function();}fn another_function() { println!("Another function.");}//运行后果:$ cargo runHello, world!Another function.2、函数传参(能够定义数据类型等): fn main() { another_function(5, 6);}fn another_function(x: i32, y: i32) { println!("传入的x为: {}", x); println!("传入的y为: {}", y);}//运行后果:$ cargo run传入的x为: 5传入的y为: 63、特地,在rust中,定义的语句不会返回值,这点跟js是一样的,例如: fn main() { let x = (let y = 6);}运行后果:`note: variable declaration using `let` is a statement`在rust中大括号{}代表一个作用域相似于js中的闭包,例如: fn main() { let x = 5; let y = { let x = 3; x + 1 }; println!("y的值为: {}", y); //运行后果:y的值为: 4}4、函数返回值的表白: rust的函数返回有两种模式,一种相似于js中的return @要返回的变量,还有一种是写在作用域最初一行不要带分号;,例如: ...

August 10, 2020 · 1 min · jiezi

关于rust:rust学习数据类型

每个变量在rust中都有特定的数据类型,大抵分为两类:标量与复合类类型。 一、标量类: 1、integer类型( i代表的取值范畴为:-2的n-1次方~2的n-1次方-1; u代表的取值范畴为:0~2的n次方-1;~~~~ ): Length Signed Unsigned 8-bit i8 u8 16-bit i16 u16 32-bit i32 u32 64-bit i64 u64 128-bit i128 u128 arch isize usize 2、Floating-Point类型:rust蕴含两种公有的float数据类型:f32和f64,例如: fn main() { let x = 2.0; // f64 let y: f32 = 3.0; // f32}数值之间的运算:rust反对根本的加减乘除数值运算,然而有一个根本的前提条件就是参加运算的数值必须是雷同类型的数据变量,其计算结果类型也是雷同的数据类型,例如: fn main() { // 加 let sum = 5 + 10; //15 // 减 let difference = 95.5 - 4.3; //91.2 // 乘 let product = 4 * 30; //120 // 除 let quotient = 56.7 / 32.2; //1.7608695652173911 let quotient = 13 / 6; //2 // 取余 let remainder = 43 % 5;}3、Boolean类型:定义rust语言中的boolean变量比较简单,能够带上标识(bool)也能够不带上此标识,例如: ...

August 10, 2020 · 2 min · jiezi

关于rust:rust学习可变变量与不可变变量

rust将变量总体分为可变与不可变变量。相似于js语言中的const与let,var等。 作为辨别可变变量须要在定义变量前增加“mut”标识,例如:let mut x = 5;此时就能够将x的值赋予一个新的值,比方:x = 6,然而跟js相比有一个限度就是数据类型不能扭转,不能说此时x新定义了一个值是小数或者字符串。如果定义时为增加mut标识的话,在编译的时候就会报错。 和js应用的区别是rust语言能够反复命名变量,相似于var,尽管应用的定义是let,例如: fn main() { let x = 5; let x = x + 1; let x = x * 2; println!("The value of x is: {}", x);}最终打印的后果为:The value of x is: 12

August 10, 2020 · 1 min · jiezi

关于rust:RustVS-CodeWSL2LLDB-安装配置调试指南

前言初学 Rust,思考了许久,打算 Rust+Neon+Node.js+Electron 开发些货色玩一玩不过在第一步配置环境上就踩了好多坑 ???? 故分享进去……不便大家第一工夫赶到现场讥笑 装置环境裂墙举荐 VS Code+WSL2,编辑器抉择 VS Code 无话可说,至于环境为啥举荐 WSL2,起因如下: Windows 环境须要装置 MSVC 生成工具,一通操作就是几个G,太轻便WSL1 对 LLDB 的反对不太行Linux 零碎对于 Rust 版本还有 Git 依赖治理起来很不便对于以上第二点,本来我也用的 WSL1,后期始终很顺利,用 GDB 调试也很失常,不过换成 LLDB 之后死活启动不了调试器,遂斗胆换成了WSL2,而后嘛……真香 尽管不晓得自试用 WSL2 两个月以来产生了什么,然而目前的 WSL2 各方面感觉都蛮优良了 配置 Rust间接参考官网教程就行了,不过可能须要长期换个源来减速装置: # 镜像减速,应用的是中科大的源,速度很快export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-staticexport RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup# 如果执意应用 WSL1,则可能须要设置这个export RUSTUP_IO_THREADS=1# 装置命令,后续选项个别默认就好了curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh# 增加到 pathsource $HOME/.cargo/env# 我应用的是 fish 须要特地设置set -U fish_user_paths ~/.cargo/bin $fish_user_paths# 更改 crates.io 为清华源,这里貌似要比中科大好一些,能够自行测试cat > ~/.cargo/config[source.crates-io]registry = "https://github.com/rust-lang/crates.io-index"replace-with = "ustc"[source.ustc]registry = "git://mirrors.ustc.edu.cn/crates.io-index"# <Ctrl+D>更改工具链的源,增加如下内容到 ~/.cargo/config 文件中 ...

August 2, 2020 · 1 min · jiezi

关于rust:学-Rust免费拿树莓派

创立并公布一个高性能 Node.js 应用程序。开始在集体我的项目或公司我的项目中应用”当红炸子鸡“ Rust 吧! 学习 Node.js、Rust、与 WebAssembly 的基础知识,参加流动,你将失去收费的树莓派套件(价值25美元)以及一个十分 cool 的证书。 本流动由 WebAssembly 中文社区与 Second State 联结主办,奖品由 Second State 资助提供,欢送在 GitHub 上理解Second State 的开源我的项目。 对于树莓派与Rust、WebAssembly 的联合,上面这篇文章能够帮忙你理解更多: 树莓派用作集体开发服务器流动规定取得收费的树莓派和证书,只须要你动点心理,本人写一段 Rust 函数。 具体步骤如下: 依照 《入门文档:在 node.js 中调用 Rust 函数》 中的示例创立一个简略的 Rust 函数,并在 Node.js app 内运行这个函数在 GitHub 上 fork 模板我的项目并更改其中的 Rust 函数,上面是几个参考示例,或者能够给你带来灵感:1 数学教育 web app2 作为 web 服务的图像识别3 机器学习和可视化 就这个利用写一个小结,形容你做了什么,并将你的我的项目分享到在朋友圈、公司技术论坛、开发者社区如思否、掘金、V2ex、CSDN、开源中国等。 到这里你曾经根本实现啦,接下来只须要通知咱们你的成绩! 在2020年8月31日前填写该表单;依据你所在的城市,咱们可能会间接安顿发货或请你从电商网站购买套件,咱们会给您报销这部分的破费金(最多25美金)。拿到树莓派后,就能够在树莓派设施上运行 Node.js 服务器和 Rust 函数啦 ! 除了树莓派套件外,您还将取得基于区块链的证书,该证书将永恒展现您的成就和开源奉献。证书如下图: 还在等什么呢?当初就开始吧! ...

July 24, 2020 · 1 min · jiezi

关于rust:连续-3-年最受欢迎Rust香

简介: 咱们在抉择一种开发语言时会综合考量各方面的个性,依据理论的需要适当取舍。鱼和熊掌往往不可兼得,要想开发效率高,必然要就义性能和资源耗费,反之亦然。然而Rust却出人意料,令人眼前一亮!本文将从性能、内存平安、开发效率、跨平台性及生态等五个方面,对Rust这一编程语言进行一些科普性质的分享。 一 性能比照不同的语言应用不同的内存治理形式,一些语言应用垃圾回收机制在运行时寻找不再被应用的内存并开释,典型的如Java、Golang。在另一些语言中,程序员必须亲自调配和开释内存,比方C/C++。Rust 则抉择了第三种形式:内存被一个所有权系统管理,它领有一系列的规定使编译器在编译时进行查看,任何所有权零碎的性能都不会导致运行时开销。Rust 速度惊人且内存利用率极高,规范Rust性能与规范C++性能并驾齐驱,某些场景下效率甚至高于C++。因为没有运行时和垃圾回收,它可能胜任对性能要求特地高的服务。网上曾经有了很多对于Rust性能剖析比照的文章,不过为了取得一手的材料,还是本人入手来的更加实在。我抉择了Python,C++,Golang这3种语言来和Rust做性能比照。 性能测试场景设计同样的算法用4种语言别离实现,比照在规定的工夫内实现工作的次数。本次测试抉择的算法是找出10000000以内的所有素数,比拟在一分钟内实现找出所有素数工作的次数。 源代码链接见[1]。 动态编译(或者打包)后生成的二进制大小比照 论断:(二进制大小)python > golang > rust > c++运行速度比照本场景下比拟1分钟内找出1000000以内所有素数的次数。 论断:(运行效率)rust > c++ > golang > python重点来了,在3台不同的机器上测试四次的结果显示:Rust效率竟然高于C++!!! 内存耗费比照(粗略计算) 论断:(内存耗费) python > golang > rust > c++CPU耗费比照(粗略计算) 论断:(CPU耗费)golang > python > rust = c++以上便是我的测试后果,测试代码、二进制和测试后果参考附件bin.zip,第一次测试后看到后果,有些吃惊,rust的性能竟然超过了c++,不堪设想,于是又在网上搜寻,找到了他人曾经实现的rust性能测试,网上的后果更让人吃惊,先看第一篇,原始链接见[2]。 我间接截图看论断: 以上为Rust vs Golang。 以上为Rust vs C++。 论断:以上截图显示,Rust在性能和资源耗费上不仅大幅度优于Golang,并且和C++性能并驾齐驱,某些场景下效率甚至优于C++。 以上两种测试场景只是测试一些简略的算法,接下来咱们看一下在理论应用中的性能资源占用比照,仍然是在网上找到了一篇测试报告[3],该测试报告用Python、PyPy、Go、Rust四种语言实现了一个web后端,接下来应用wrk别离对四个http服务器进行压测,该测试场景比拟贴近理论,间接截图看论断: 论断(性能):在理论作为后端服务应用的场景下,Rust比Golang仍然有显著性能劣势。 论断(资源占用):在内存占用上Rust的劣势更加显著,只用了Golang的1/3。 综合以上3个测试,Rust在运行效率和资源耗费上的劣势非常显著,和C++同一个级别,远远优于Golang ! 二 内存安全性Rust 最重要的特点就是能够提供内存平安保障,而且没有额定的性能损失。在传统的零碎级编程语言( C/C++) 的开发过程中,经常出现因各种内存谬误引起的解体或bug ,比方空指针、野指针、内存透露、内存越界、段谬误、数据竞争、迭代器生效等,血泪斑斑,不可胜数;内存问题是影响程序稳定性和安全性的重大隐患,并且是影响开发效率的重大因素;依据google和微软 两大巨头的说法,旗下重要产品程序平安问题70%由内存问题引发[4], 并且两个巨头都用利用Rust语言来解决内存平安问题的想法。Rust语言从设计之初就把解决内存平安作为一个重要指标,通过一系列伎俩保障内存平安,让不平安的潜在危险在编译阶段就裸露进去。接下来依据本人浅显的了解,简略介绍Rust解决内存平安的伎俩有哪些。 ...

July 21, 2020 · 7 min · jiezi

WebAssembly-一周一报0701

Wasm 中文交流群请加微信号 h0923xw将 Wasm 字节码编译成 C 代码,用于通用的字节码 ! ????Alon Zakai,WebAssembly 的创始人,开始了一个新项目, wasm2c。 顾名思义,wasm2c 的目标是将 Wasm 字节码编译成C (portable C) ,然后可以将其编译成任何计算机上的本机可执行文件。 为什么? 他希望 Wasm 在浏览器之外也能发挥作用,但是 WASI 和 Node.js 的扩展进度仍然缓慢。 由 Wasm 编译的可移植 C 具有内置的完善的 VM 安全保护,而且 C 编译器工具(如 GCC)几乎是“普遍”可用的。 这绝对是一个值得持续关注的有趣想法。 Deno 支持 WASI ????Deno本周发布了 V1.1.2。重大新闻是 Deno 现在在其内置的 WebAssembly 引擎中支持WASI。 WebAssembl y正在成为 Deno 生态的一等公民! Fastly 在 WebAssembly 生态中大量投入 ????Fastly 写了一篇很棒的博客文章,介绍其历史以及为支持开源 WebAssembly 软件和标准所做的持续努力。您可以在 Fastly 的 WebAssembly 基础结构上运行无服务器功能。 Vite 支持内嵌 WebAssembly ????Vite 发布了 v1.0 beta,支持内置的WebAssembly。开发者可以简单地导入预编译的 WebAssembly 字节码文件并调用其函数。 Vite 是本机 ES 模块支持的 web 开发构建工具. ...

July 2, 2020 · 2 min · jiezi

WebAssembly-一周一报0623

Webassembly 相关技术的更新和评论,包括 Rust、 serverless、 cloud、 blockchain 和 AI。WebAseembly 入群请至文末用 Rust 写的 Kubernetes for WebAssembly我们之前已经讨论过用 WebAssembly 代替 Docker 容器 ,现在我们朝着这个愿景又迈进了一步。 它允许 Kubernetes 管理 WebAssembly VMs 和 Docker 容器! 最赞的地方是用Rust写的! 数据科学的未来是 JavaScript 和 WebAssemblyRob Blackbourn 写了一系列文章来论证数据科学的未来是 JavaScript 和 WebAssembly。 这个项目还处于非常早期的阶段,但是在 WebAssembly 上运行 FORTRAN ?非常期待! 以下是他提到的原因: JavaScript 可以提供优雅的语法WebAssembly 可以提供接近原生的速度可移植:用 JS 和 Wasm 编写的应用程序可以运行在任何浏览器和服务器端的 JS 引擎中,比如 Node.jsWebAssembly 得到了 C 的良好支持Wasm streams: 连接 web streams 和 Rust streams我们已经见过了从Rust 源代码调用 JavaScript 函数的方法,但是这个新项目脱颖而出,因为它演示了通过 Futures crate 异步调用 JavaScript 函数的方法。 ...

June 24, 2020 · 2 min · jiezi

在windows10家庭版1909上设置rust1440的eclipse开发环境

rust是一个很牛逼的语言,在stackoverflow上连续三年被评为最受欢迎的语言。但是,真正用起来的酸爽也只有用的人自己知道。 简单写个hello world,当然不会遇到什么挑战。但是工作中,尤其是实现复杂业务逻辑的时候,是不是好用,很大程度上和IDE有关,尤其是debug。这是本人的体会。windows平台的开发环境设置,我试了几次,都磕磕绊绊的。 本文记录了我在一台华为开发本上的环境搭建过程,以备将来参考。 一、软件列表: rust官方安装包(https://www.rust-lang.org/learn/get-started)eclipse for rust(https://www.eclipse.org/downloads/packages/release/2020-06/r/eclipse-ide-rust-developers)gdb安装包(https://astuteinternet.dl.sourceforge.net/project/tdm-gcc/GDB/gdb-7.9.1-tdm64-2.zip) 二、安装rust官方包 首先,下载RUSTUP-INIT.EXE (64-BIT)](https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe) image.png 此安装包依赖Visual Studio的C++。如果在安装rust时,没有安装Visual Studio,会有提示。可以根据提示信息安装后,再继续。我选择的是 Visual Studio 2019 Community,安装的组件是C++桌面开发。如下图所示: image.png 在rustup-init.exe的安装界面,注意要选择2定制,不要默认安装。 image.png 选择2)Customize installation。然后在提示语: Default host triple?下面,输入:x86_64-pc-windows-gnu 这是关键的一步。后面的,都可以直接回车。再次回到上边的选择页面时,直接回车,开始安装。 安装完毕后,简单运行: rustup --version。如看到版本信息,则说明此步安装成功。 三、下载并安装eclipse for rust 我下载的是2020-06的版本。eclipse需要java环境运行。如果没有,需要安装。建议安装jdk8。 将下载的eclipse压缩包解压到一个目录,即可。直接双击exlipse.exe文件,就能运行。 四、安装gdb包 根据stackoverflow上大牛们的建议,直接下载TDM版的GDB。原文链接在此:https://stackoverflow.com/questions/33570021/how-to-set-up-gdb-for-debugging-rust-programs-in-windows 简单讲,下载下来的zip文件,解压到一个固定的目录即可。 五、启动eclipse,完成最后的设置 新建一个rust项目。在main.rs文件中混乱写两行代码,加个断点。开始debug。 eclipse提示有错误。查看详细信息。看到提示“rls --version”没有相应。解决办法,开个命令行,执行命令:rustup component add rls --toolchain x86_64-pc-windows-gnu 之后,关闭eclipse,再重启。进入项目的debug设置界面,再debugger页面,选择前面下载的tdm gdb的可执行文件的位置。如图所示: image.png 再此debug,成功! 有图有真相: image.png 后记,N年没有使用eclipse了。这次,因为rust,再此拥抱eclipse,多少勾起了一些当年Java开发的回忆。 哈哈哈

June 18, 2020 · 1 min · jiezi

Rust-Study-RoadMap

背景近几个月,笔者开始学习Rust,并用Rust开始写一些代码。学到现在,不说对Rust有很深的理解,但是日常用来写一些代码是没有问题的,并且也能够根据需要进行下一步的学习。在学习的过程中,笔者也看了许多的资料,但是始终没有找到一个清晰的学习路径,可以帮助在学和写之间配合起来,所以笔者根据自己的学习经验,制作了这一份Rust的学习路径,配合一些实践和测试题,帮助大家学习Rust。同时笔者也在这里推荐一下PingCAP的学习课程,其中的Rust课程质量很高,但是需要有一定的Rust基础,所以这也是笔者将其放在RoadMap最后面的原因,有经验的读者可以直接跳过前面进行PingCAP课程的学习 本路线并不是最终版本,日后会根据各种资源和需求的变化进行调整 RoadMap One此路线比较适合习惯先了解全貌再去实践的同学,在入门级的课程中不包括各种Rust的例子,而是旨在了解Rust的语法、语义、类型系统等,它与RoadMap Two的区别在于,学习资料的顺序是颠倒的,实际上的知识点没有区别 基础知识 先阅读The Rust Reference,重点了解Rust的各种语法和语义结构,有一个大概的认知参照Rust Language Cheat Sheet,对步骤一进行总结阅读The Rust Programming Language,重点放在各种特性以及例子的实现跟随Rust by Example,结合步骤三进行实践完成本节课程测试题 测试题没有先后区分,只要能够完成一道题就可以进行后面的学习 使用Rust编写一个LinkList, 要求所有的节点需要分配在堆上,需要实现pop、push方法使用Rust编写双链双端队列以及对应的方法扩展阅读 扩展阅读部分是一些Rust的借用和所有权的一些文章,用来帮助读者了解相关知识 rust-means-never-having-to-close-a-socketFearless-ConcurrencyRAIIRoadMap Two此路线比较适合习惯先实践的同学,但是因为Rust与平常我们学习的语言有较大出入,我不建议直接进行实践。所以此路线还是会先进行Rust各项特性、基础的学习,但是会在学习的过程中让学习者进行实践操作,它与RoadMap One的区别是,学习资料的顺序是颠倒的,实际上的知识点没有区别 基础知识 阅读The Rust Programming Language,重点放在各种特性以及例子的实现阅读The Rust Reference,重点了解Rust的各种语法和语义结构,有一个大概的认知参照Rust Language Cheat Sheet,对步骤二进行总结跟随Rust by Example,进行实践完成本节课程测试题 测试题没有先后区分,只要能够完成一道题就可以进行后面的学习 使用Rust编写一个LinkList, 要求所有的节点需要分配在堆上,需要实现pop、push方法使用Rust编写双链双端队列以及对应的方法扩展阅读 扩展阅读部分是一些Rust的借用和所有权的一些文章,用来帮助读者了解相关知识 rust-means-never-having-to-close-a-socketFearless-ConcurrencyRAII进阶完成路线一或者路线二的学习之后,可以进行进阶的学习,进阶部分不需要再学习新的资料,只需要完成两项训练即可,如有兴趣,可以再进行扩展部分的学习(强烈推荐)。 目标 可以使用Rust进行日常开发,熟悉Rust的各项特性、语法和语义,使用Cargo进行项目搭建,代码检测,版本发布 内容 rustlings训练 阅读并实践本书前6节 扩展 扩展部分强烈建议学习,本扩展是用来学习Rust配套的Cargo工具。Cargo除了可以用作包管理外还有其他许多强大的功能可以帮助开发者开发Rust程序。 PS: 本节内容没有测试 最终目标完成PingCAP本门课程 额外部分,选修Rust异步编程,阅读本书UnSafe Rust, 阅读本书Rust 宏, 阅读本书Rust 的编程范式,本书

June 7, 2020 · 1 min · jiezi

qlv转mp4格式转换器哪个好用

迅捷视频转换器是一款简单的视频转换软件,可以帮助用户在使用软件转换视频格式的时候获得更好的操作,该软件可以解决qlv转MP4格式的问题,qlv这种格式的视频不常见,普通的播放器不能兼容这种格式,导致很多用户无法查看qlv里面的内容,所以小编找到了这款腾讯视频qlv转换MP4工具,通过该软件就可以将qlv转换MP4,这样你的视频就可以在很多播放器查看了,如果你遇到不能播放qlv的情况,建议你先去试试这款迅捷视频转换器软件!好不好用你说了算。下面小编给大家简单讲解一下这款视频转换器的操作步骤。安装好这款软件之后,双击打开这款软件,就可以看到这款软件的功能列表,其中功能有视频转换、视频分割、视频美化、视频配乐等等功能,看起来功能种类繁多,不太好操作的样子,其实操作起来却非常简单。单击选择视频转换功能,点击软件中的添加文件,加入想要转换格式的视频文件文件。添加完文件之后,软件下方会出现输出格式和输出路径,格式选择MP4格式,分辨率一般是默认的,输出路径也可以选择默认的。最后进行格式转换就可以了,最后一步可以通过输出格式右边的全部转换来完成qlv和MP4的转换。操作流程大概就是这个样子,qlv转mp4格式转换器可以帮助你们解决腾讯qlv格式的困扰哦!试一试不吃亏,总要试一试才知道我说的是真是假啊!

October 17, 2019 · 1 min · jiezi

暑期特别企划-快来接收-PingCAP-Talent-Plan-的小惊喜

PingCAP Talent Plan 学习通道自开通以来,收获了海内外小伙伴的密切关注,有 100 余名小伙伴参与到线上课程的学习中,第二期线下课程也于 5 月中旬圆满落幕。结合大家的意见,我们对 Talent Plan 的课程做了一些优化,并推出 Talent Plan 暑期特别企划,线上课程和线下课程都增加了一些新的元素~大家快来接收这一波“小惊喜”吧!线上课程1. Practical Networked Applications in Rust 全面开放我们发现很多开发者都愿意参与 TiKV 的研发,但通常都会遇到两个困难,第一是不会 Rust 语言,因为这门语言的门槛实在太高了,第二是没有分布式数据库相关的理论知识,不知道如何用 Rust 写一个分布式高性能服务。虽然现在市面上有很多的 Rust 教程,但大多数是集中在语言本身的教学上面,所以我们决定在它们的基础上,专门推出一套新的 Rust 培训课。基于这方面的考虑,Rust 核心作者 Brian Anderson 对 Rust 课程进行重新设计,推出 Practical Networked Applications in Rust (https://github.com/pingcap/talent-plan/tree/master/rust),并向社区小伙伴全面开放。 通过这门课程,大家不仅能学到 Rust 的基本知识,还能使用 Rust 来构建自己的存储引擎和网络框架,学习如何写高性能的并发程序,从而真正进入使用 Rust 来进行分布式系统开发的大门。 温馨提示:对该课程提出改进意见的小伙伴,我们会结合意见及改进情况给予额外的加分哦!2. 线上作业提交方式变更:由集中打包提交改为分批次提交线上课程开放之初,作业提交采用的是集中打包的方式,这么做的目的是为了使作业更具连贯性,在进行作业评估的时候,也能够更全面的了解大家对于线上课程的掌握程度。但是运行了一段时间之后,我们发现,大部分小伙伴基于学业及工作方面的考虑,学习课程的时间相对分散,于是我们将线上课程提交方式改为分批次提交,一方面是为了更好地适应大家的学习节奏,另一方面也可以通过作业提交情况了解大家的学习进度以及在学习中遇到的问题,以便针对性地对课程进行调整并组织集中答疑。更新后的线上作业提交方式如下: 发送邮件至 ts-team@pingcap.com邮件主题:【PingCAP Talent Plan】申请线上课程作业评估+申请人+联系方式。正文: 请简单介绍自己(包括姓名、GitHub ID、常用联系方式等)。在校学生需注明所在高校、年级和专业等信息;非在校学生需注明当前就职公司、是否能 full-time 参与 4 周线下课程等。以附件形式提交作业。线上作业提交通道每周六 0:00 开启,至周日 24:00 关闭,持续 48h 开放。作业可以「完成多少就提交多少」,但要以周为单位(如果某一周的作业只完成了一部分,可以放到下个提交通道开启时提交)。线下课程完成线上课程并通过考核的同学将有机会参加线下课程。第三期线下课程正值暑期,为了帮助同学们充分利用暑假时间,更好地参与和熟悉开源社区,我们对第三期线下课程做了大量调整。调整后的线下课程包括 1 周的集中授课阶段以及 3 周的实战演练阶段。 ...

July 3, 2019 · 2 min · jiezi

Tensorflow-Rust实战下篇整合actixweb提供http服务

上一篇我写的文章how to use tensorflow with rust. 这一次我们看看使用tensorflow建立了什么,并通过http接口提供服务。随着Actix Web1.0版本发布,我认为用它构建一些东西将是一个很好的时机。 本文假设您对Futures及其运作方式有一定的了解。我将尽量用更简单的术语解释,但理解Futures生态系统将非常有效地帮助阅读本文。为此,我建议你从tokio开始。 有些人建议在深入Futures之前等待async/await和friends功能发布。我认为你现在应该亲自动手:异步编程总是很有挑战性。再一次为了不耐烦的人,您可以在actix-web分支上找到参考代码:https://github.com/cetra3/mtc... 一、API定义这里的API非常简单。我们想模仿我们在命令行上所做的事情:提交一张图片,返回结果是一张图片。为了使事情变得有趣,我们将提供一种方式:将边界框以JSON数组返回。 关于通过http协议提交二进制数据,我首先想到了几种选择: 只需提交原始数据即可使用multipart/form-data序列化为JSON格式提交我认为最简单是原始数据,所以让我们这样做! multipart/form-data可能ok,但是你必须处理多个图像的时候呢? JSON格式似乎有点浪费,因为您不可避免地必须使用base64或类似的方式转换二进制数据。 所以我们的API是这样的: 提交POST请求作为原始文件提交运行会话,通过MTCNN算法以提取人脸将边界框作以JSON格式返回;或者命令行示例一样,将图像叠加以JPEG格式返回。二、MTCNN的结构体(struct)在我们上一篇博客中,我们只是简单地使用main函数来执行所有操作,但我们必须一些重构才能与actix一起使用。我们希望将MTCNN行为封装为结构,可以传递和转移。最终目标是在应用程序状态下使用它。 2.1结构体(struct)定义让我们将结构包含我们想要的一切: 图版会话一些多个请求中共用的Tensor框架的输入参数首先,我们创建一个新文件mtcnn.rs并加上结构体定义。 use tensorflow::{Graph, Session, Tensor};pub struct Mtcnn { graph: Graph, session: Session, min_size: Tensor<f32>, thresholds: Tensor<f32>, factor: Tensor<f32>}然后,现在我们只是用new方法填充初始化内容。由于其中一些值的初始化并非绝对可靠,我们将返回Result: pub fn new() -> Result<Self, Box<dyn Error>> { let model = include_bytes!("mtcnn.pb"); let mut graph = Graph::new(); graph.import_graph_def(&*model, &ImportGraphDefOptions::new())?; let session = Session::new(&SessionOptions::new(), &graph)?; let min_size = Tensor::new(&[]).with_values(&[40f32])?; let thresholds = Tensor::new(&[3]).with_values(&[0.6f32, 0.7f32, 0.7f32])?; let factor = Tensor::new(&[]).with_values(&[0.709f32])?; Ok(Self { graph, session, min_size, thresholds, factor })}2.2Run方法我将在这里开始加快节奏,所以如果你遇到困难或不确定发生了什么,请查看face-detection-with-tensorflow-rust,以解释这里发生的事情。我们已经添加了所有需要跑一个会话的东西。让我们创建一个需要API做什么的方法:提交一张图片,响应一些边界框(框出人脸的位置): ...

June 29, 2019 · 6 min · jiezi

Rust的所有权

作为一个Ruby开发者,我所知道的关于内存分配的所有内容都是由一些称为垃圾收集的process处理的,这是Aaron Patterson的问题,而不是我的问题。因此,当我打开Rust Book并看到Rust没有垃圾收集时,我变得有点担心。处理记忆管理的责任是否应该堆积在我身上?显然,对于C这样的系统编程语言来说,处理内存分配是一件大事,如果做得不好就会产生重大影响。 1、Stack(栈) & Heap(堆) 栈和堆是在运行时管理内存的方法。栈被认为是快速高效的,因为它有序地存储和访问数据。栈顶是在栈中添加、删除数据的唯一位置。这被称为LIFO,后进先出,意味着我们在释放内存时只需知道栈顶的地址。栈快速的另一个原因,栈所需的内存空间在编译时是已知的。这意味着我们可以在将数据存储到其中之前分配一块固定大小的内存。例如,如果你知道有四个人参加您的晚宴,你可以提前决定每个人的座位,准备多少食物,并在他们到达之前练习他们的名字。这是超级高效的!如果你不能提前确切地知道有多少人参加您的晚宴,你可以使用堆。使用堆意味着需要准备足够多但未知数目的椅子并向到来的人发出名称标签(name字段)。 当在运行时期间需要存储未知大小的数据时,计算机会在堆上搜索内存,对其进行标记并返回一个指针,该指针指向内存中的位置。这称为分配内存。然后,您可以在栈上使用该指针,但是,当你要检索真实数据时,需要读取指针指向的内存位置的数据。 当我不断深入了解栈和堆时,管理堆中的数据会很困难。例如,你需要确保在完成使用一块内存后允许计算机重新分配这块内存。但是,如果其中一个代码块释放了内存中的某个位置,而另一个代码块仍然持有该内存的指针,则会出现悬空指针(dangling pointer)。 跟踪哪部分代码正在使用堆上的哪些数据,最小化堆上的数据拷贝,以及清理堆上未被使用的数据使其不会耗尽内存空间,这是所有权解决的所有问题。Rust Book2、Ownership(所有权) & Scope(作用域)Rust关于所有权的三个规则: Rust中的每个值都有一个称为其所有者的变量名。(let name = "xxx")同一时间只能有一个所有者。当所有者超出作用域时,该值将被删除。2.1所有权的最简单的例子所有权的最简单的例子是关于变量的作用域:一旦当前函数作用域结束,由}表示,变量hello超出作用域会被删除。这一点和大多数编程语言的本地变量是一致的。但这不是所有权的全部,当我们需要在传递值,并将字符串常量(暂时认为数据存储在栈中,其实在永久的常量池中)切换到String类型(数据存储在堆上的)时,事情会变得更有趣。 2.2所有权发生改变当使用字符串常量时,正如我们所期望的那样,Rust将hello的值复制到hello1中。 但是当使用String类型时,Rust会移交该值的所有值。编译时会抛出错误:error[E0382]: use of moved value: 'hello’。 看起来在使用字符串常量时,Rust会将该一个变量的值复制到另一个变量中,但是当我们使用String类型时,它会移交所有权。关于这个话题在论坛里有相关讨论,请阅读此贴子 The Copy trait - what does it actually copy?. 以下是我对讨论后的总结:字符串常量“Hello,World!”存储在只读内存中的某个位置(既不在栈中也不在堆中),并且指向该字符串的指针存储在栈中。 这里的指针通常是称为引用,这意味着我们使用指向存储在永久内存中的字符串常量的引用(参见Ownership in Rust, Part 2中有关引用的更多信息),并保证它在整个程序的运行时间里是有效的(它有一个静态的生命周期)。 变量hello和hello1存储在栈。 当我们使用=运算符时,Rust会将存储在hello中的指针值的副本绑定到变量hello1。 在作用域的最后,Rust会调用drop方法从栈中删除变量以释放内存。 这些变量可以存储并轻松地在栈中进行复制,因为它们的大小在编译时是已知的。 在堆上,字符串类型的值为“hello,world!”使用 string:from 方法绑定到变量hello。但是,与字符串常量不同,绑定到变量hello的是数据本身而不仅仅是指针,并且这些数据的大小可以在运行时更改。=运算符将变量hello指向的数据绑定到新变量hello1,有效地将数据的所有权从一个变量移交给另一个变量。变量hello现在是无效的,根据所有权规则2:“同一时间只能有一个所有者。” 2.3为什么不总是copy数据?但为什么这样呢?为什么Rust不始终复制数据并将其绑定到新变量?回想一下栈和堆之间的差异,堆上存储的数据大小在编译时是不可知的,这意味着我们需要在运行时进行一些内存分配步骤。这可能会代价很高。根据我们的数据量,如果我们整天都在copy数据,可能会很快耗尽内存。除此之外,Rust的默认行为会保护我们免受内存问题的影响(可能在其他语言中遇到)。 将数据存储在堆上并在栈上存储指向该数据的指针。但是,与使用指针指向只读内存(存储字符串常量)不同,堆上的数据可能会发生变化。指针值<< DATA >>绑定到存储String类型的hello变量。如果我们将相同的指针值绑定到两个不同的变量,看起来像这样: 我们有两个变量hello和hello1,它们共享相同值的所有权。 这违反了规则2:“同一时间只能有一个所有者”,但让我们继续。在变量hello和hello1的作用域结束时,我们必须将他们在堆上的内存释放。 首先,我们将hello1指向的堆上内存数据释放,现在当我们释放hello时会发生什么? 这称为双重释放错误(double free error),我认为在这个StackOverflow答案中有最好的总结:https://stackoverflow.com/a/2... A double free in C, technically speaking, leads to undefined behavior. This means that the program can behave completely arbitrarily and all bets are off about what happens. That’s certainly a bad thing to have happen! In practice, double-freeing a block of memory will corrupt the state of the memory manager, which might cause existing blocks of memory to get corrupted or for future allocations to fail in bizarre ways (for example, the same memory getting handed out on two different successive calls of malloc).Double frees can happen in all sorts of cases. A fairly common one is when multiple different objects all have pointers to one another and start getting cleaned up by calls to free. When this happens, if you aren't careful, you might free the same pointer multiple times when cleaning up the objects. There are lots of other cases as well, though.— templatetypedefRust就是要避免犯这类错误。通过使hello无效,编译器知道只在hello1上发出一个释放内存的调用(drop)。 ...

June 23, 2019 · 2 min · jiezi

TiKV-源码解析系列文章八grpcrs-的封装与实现

作者: 李建俊 上一篇《gRPC Server 的初始化和启动流程》为大家介绍了 gRPC Server 的初始化和启动流程,本篇将带大家深入到 grpc-rs 这个库里,查看 RPC 请求是如何被封装和派发的,以及它是怎么和 Rust Future 进行结合的。 gRPC C CoregRPC 包括了一系列复杂的协议和流控机制,如果要为每个语言都实现一遍这些机制和协议,将会是一个很繁重的工作。因此 gRPC 提供了一个统一的库来提供基本的实现,其他语言再基于这个实现进行封装和适配,提供更符合相应语言习惯或生态的接口。这个库就是 gRPC C Core,grpc-rs 就是基于 gRPC C Core 进行封装的。 要说明 grpc-rs 的实现,需要先介绍 gRPC C Core 的运行方式。gRPC C Core 有三个很关键的概念 grpc_channel、grpc_completion_queue、grpc_call。grpc_channel 在 RPC 里就是底层的连接,grpc_completion_queue 就是一个处理完成事件的队列。grpc_call 代表的是一个 RPC。要进行一次 RPC,首先从 grpc_channel 创建一个 grpc_call,然后再给这个 grpc_call 发送请求,收取响应。而这个过程都是异步,所以需要调用 grpc_completion_queue 的接口去驱动消息处理。整个过程可以通过以下代码来解释(为了让代码更可读一些,以下代码和实际可编译运行的代码有一些出入)。 grpc_completion_queue* queue = grpc_completion_queue_create_for_next(NULL);grpc_channel* ch = grpc_insecure_channel_create("example.com", NULL);grpc_call* call = grpc_channel_create_call(ch, NULL, 0, queue, "say_hello");grpc_op ops[6];memset(ops, 0, sizeof(ops));char* buffer = (char*) malloc(100);ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;ops[1].op = GRPC_OP_SEND_MESSAGE;ops[1].data.send_message.send_message = "gRPC";ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;ops[3].op = GRPC_OP_RECV_INITIAL_METADATA;ops[4].op = GRPC_OP_RECV_MESSAGE;ops[4].data.recv_message.recv_message = buffer;ops[5].op = GRPC_OP_RECV_STATUS_ON_CLIENT;void* tag = malloc(1);grpc_call_start_batch(call, ops, 6, tag);grpc_event ev = grpc_completion_queue_next(queue);ASSERT_EQ(ev.tag, tag);ASSERT(strcmp(buffer, "Hello gRPC"));可以看到,对 grpc_call 的操作是通过一次 grpc_call_start_batch 来指定的。这个 start batch 会将指定的操作放在内存 buffer 当中,然后通过 grpc_completion_queue_next 来实际执行相关操作,如收发消息。这里需要注意的是 tag 这个变量。当这些操作都完成以后,grpc_completion_queue_next 会返回一个包含 tag 的消息来通知这个操作完成了。所以在代码的末尾就可以在先前指定的 buffer 读出预期的字符串。 ...

June 13, 2019 · 4 min · jiezi

rust-状态机设计模式

什么是状态机状态机无处不在,像我们常用的 tcp、http、regexp 等等本质上都是状态机。 状态机是由状态和状态间的转换构成的。 拿红绿灯来举个简单的例子,红绿灯会处于 3 个状态:红、绿、黄,这几个状态之间有确定的转换路径: +-----------+      +------------+      +---------+|   Green   +----->+   Yellow   +----->+   Red   |+-----+-----+      +------------+      +----+----+      ^                                     |      |                                     |      +-------------------------------------+如果用 rust 来写的话,我可能会这样实现: ...

June 9, 2019 · 3 min · jiezi

RustCon-Asia-实录-Rust-在国内某视频网站的应用

作者介绍:hawkingrei(王维真),中间件高级开发工程师,开源爱好者,TiDB & TiKV Contributor。WaySLOG(雪松),Rust 铁粉一枚,专注中间件,bug creator。 本文根据 hawkingrei & WaySLOG 在 首届 RustCon Asia 大会 上的演讲整理。 今天我们会和大家聊聊 Rust 在我们公司的二三事,包括在公司产品里面用的两个工具,以及雪松(WaySLOG)做的 Cache Proxy —— Aster 的一些经验。 <center>图 1</center> 十年前,我司刚刚成立,那时候其实很多人都喜欢用 PHP 等一些动态语言来支持自己的早期业务。用动态语言好处在于开发简单,速度快。但是动态语言对代码质量、开发的水平的要求不是很高。所以我来到公司以后的第一个任务就是把我们的 PHP 改写成 Golang 业务。在我看了当时 PHP 的代码以后的感受是:动态语言一时爽,代码重构火葬场。因为早期我司还是个人网站,PHP 代码质量比较差,代码比较随意,整套系统做在了一个单体的软件里,我们称这个软件是一个全家桶,所有的业务都堆在里面,比较恶心。所以导致早期我司的服务质量也是非常差,观众给我们公司一个绰号叫「小破站」。 但是随着规模越来越大,还上市了,如果还停留在「小破站」就十分不妥,因此我们开始用 Golang 对服务进行一些改进,包括开发一些微服务来稳定我们的业务。通过这些改造也获得了很好的一个效果,因为 Golang 本身非常简洁,是一个带 GC 的语言,同时还提供了 goroutine 和 channel 一些功能,可以很方便的实现异步操作。但随着业务规模变大,也出现了一些 Golang 无法支持的一些情况。于是,我们将目光转向了 Rust。 1. Remote CacheRemote Cache 是我们第一个 Rust 服务。该服务是我们公司内部的一套 Cache 服务。 在具体介绍这个服务之前,先介绍一下背景。首先在我们内部,我们的代码库并不像普通的一些公司一个项目一个库,我们是大仓库,按语言分类,把所有相同语言的一个业务代码放到一个仓库里,同时在里面还会封装一些同一种语言会用到的基础库,第三方的依赖放在一个库里面。这样所有的业务都放在一个仓库,导致整个仓库的体积非常巨大,编译也会花很多的时间,急需优化。 <center>图 2</center> 此时,我们用到了两个工具—— Bazel 和 Gradle,这两个编译工具自带了 Remote Cache 功能。比如你在一台机器上编译以后,然后换了台机器,你还可以重新利用到上次编译的一个中间结果继续编译,加快编译的速度。 ...

June 4, 2019 · 4 min · jiezi

使用Rust和Elixir实现高效的下发好友列表

去年,Discord的后端基础设施团队努力提高核心实时通信基础设施的可扩展性和性能。 我们进行的一个大项目是改变我们更新会员列表的方式(屏幕右侧的那些漂亮的头像)。我们可以直接发送会员列表中可见部分的更新(分页),而不是为会员列表中的每个人发送更新。这样做的好处很明显,例如网络流量更少,CPU使用率更低,电池寿命更长等等。 然而,这在服务器端造成了一个大问题:我们需要一个能够容纳数十万个条目的数据结构,以一种可以处理大量更新的方式进行排序,并且可以上报会员的位置索引添加和删除。 Elixir是一种函数式语言,它的数据结构是不可变的。这对推理代码并支撑大量并发性都非常好。不可变数据结构的双刃剑。现有的数据结构的更新是通过创建全新数据结构来实现的,该全新数据结构是将该操作应用于现有的数据结构的结果。 这意味着当有人加入服务器(内部称为公会)并拥有100,000名成员的成员列表时,我们必须构建一个包含100,001名成员的新列表。 BEAM VM非常快速,并且每天都在变得更快。Elixir试图在可能的情况下利用persistent data structure。但是在我们的运营规模下,这样的更新效率是无法被接受的。 将Elixir推至极限两位工程师接受了制作纯Elixir数据结构的挑战,该数据结构可以容纳大型sorted sets并支持快速更新操作。这说起来容易做起来难。 Elixir附带一个名为MapSet的set实现。 MapSet是构建在Map数据结构之上的通用数据结构。它对许多Set操作很有用,但它不能保证有序,但这是成员列表的关键要求,排除MapSet。 接下来将是古老的List类型:对List做一层封装,强制保证唯一性并在插入新元素后对列表进行排序。这种方法的压测数据表明,对于小型列表(5,000个元素) ,插入时间在500s和3,000s之间。这太慢了,不可行。更糟糕的是,插入的性能与列表的大小和列表中的位置深度成正比。在250,000个元素的末尾添加一个新元素,大约170,000s:基本上是恒定的。 两次失败,但BEAM尚未退出竞争。 Erlang附带一个名为ordsets的模块。 Ordsets是有序sets,所以听起来我们找到了解决问题的方法:让我们压测一下以检查可行性。当列表很小时,性能看起来相当不错,测量范围在0.008s和288s之间。遗憾的是,当测试的大小增加到250,000时,最坏情况下的性能提高到27,000s,这比我们的自定义List实现速度提高了五倍,但仍然不够快。 尝试了语言附带的所有候选者,粗略地搜索了lib,看看其他人是否已经解决了这个问题并开源。看了一些lib,但它们都没有提供所需的属性和性能。值得庆幸的是,计算机科学领域一直在优化用于存储和分类数据的算法和数据结构,因此有很多关于如何进行的想法。 SkipListordset在小数据下表现非常出色。也许有一些方法可以将一堆非常小的ordsets链接在一起,并在访问特定位置时快速访问正确的ordset。这类似于一个skiplist。 这个新数据结构的第一个版本非常简单。 OrderedSet是一个Cell列表的封装,每个Cell内部都是一个小的ordset:ordset的第一项,ordset的最后一项,以及count。这允许OrderedSet快速遍历Cells列表以找到适当的Cell,然后执行非常快速的ordset操作。在250,000项目列表的末尾插入项目从27,000s降至5,000s,比原始ordsets快5倍,比原始List实现快34倍。 性能有所提升,但是在列表的头部Cell创建250,000个元素,单个插入时间仍为19,000s。 这是有道理的。当你在OrderedSet的前面插入一个项目时,它会在第一个Cell中结束,但是Cell已经满了,所以它将它的最后一个项目驱逐到下一个Cell,但是Cell已经满了,所以它将它的最后一个项目驱逐到下一个Cell,依此类推。 OrderedSet问题在于,当元素填满时,操作会从Cell级联到下一个Cell。如果我们允许Cell分裂,在列表中间动态插入新Cell呢?好处是:最坏的情况是Cell分裂,而不是级联。 优化后的情况:在小列表时,这个新的OrderedSet可以在列表中的任何点执行4s和34s之间的插入,不错。我们将尺寸调整到250,000。在列表的开头插入,第一个插入为4s,后面会逐惭变慢。最终在列表末尾插入一个项目需要640s,看起来还行。 必须更快!上面的解决方案适用于高达250,000名成员的公会,但我们想要更多!Discord一直在使用Rust来让事情变得更快,我们可以使用Rust来加快速度吗? Rust不是一种函数式语言,可以使用可变数据结构。它也没有运行时并提供“zero-cost abstractions”。如果我们用Rust,它可能会表现得更好。 我们的核心服务不是用Rust编写的,它们是基于Elixir的。 Elixir非常适合调用Rust,幸运的是,BEAM VM还有另一个漂亮的技巧。 BEAM VM有三种类型的函数: 用Erlang或Elixir编写的函数。这些是简单的用户空间函数。内置于语言中的函数,充当用户空间函数的构建块。这些被称为BIF或内置函数。NIF或native函数。这些是使用C或Rust构建并编译到BEAM VM中的函数。调用这些函数就像调用BIF一样,但是你可以控制它的功能。有一个名为Rustler的Elixir项目。它为Elixir和Rust提供了很好的支持,可以创建一个表现良好的安全的NIF,并保证使用Rust不会VM崩溃或内存泄漏。 我们预留了一个星期,看看这是否值得付出努力。到本周末,我们给出一个非常有限的概念验证。压测数据看上去很有希望,与OrderedSet的4s至640s相比,向SortedSet添加元素的最佳情况是0.4s,最差情况为2.85s。这只是使用integer的测试,但它足以证明优于Elixir的实现。 有了数据支撑,我们决定继续扩展程序支持更多的Elixir数据类型。最后我们的测试数据如下:我们将数量一直增加到1,000,000。最后打印出结果:SortedSet最佳情况为0.61s,最差情况为3.68s,其基于多种大小的sets,大小从5,000到1,000,000。 我们使最坏的情况与先前的迭代最佳情况一样好!Rust支持的NIF提供了巨大的性能优势,而无需牺牲易用性或内存。 喜讯今天,Rust版的SortedSet为每一个Discord公会提供支持:从计划到日本旅行的3人公会到享受最新、有趣的游戏的20万人公会。 自布署SortedSet以来,我们已经看到性能全面提升,不会对内存压力产生影响。我们了解到Rust和Elixir可以并肩工作。我们仍然可以将我们的核心实时通信逻辑保留在更高级别的Elixir中,它具有出色的保护和简单的并发实现,同时在需要时可以使用Rust。 如果你需要一个高效更新的SortedSet,我们已经开源了SortedSet。

June 1, 2019 · 1 min · jiezi

CKB-测试网-Rylai-上线之后你可以玩些什么

正如 5 月 18 日发布的《Nervos CKB 测试网正式上线》里说的一样,Rylai 经过了三十多次的迭代,我们在全球通过购买服务器,部署了真实的节点,限制了带宽,所有的测试都在真实的网络上发生。这期间的测试过程中出现了很多问题,但很幸运,我们也都解决了这些问题。(谢谢冰女保佑????) 为什么叫 Rylai?很多人问起名字的来源,终于在 Testnet Launch Party 上,Terry 做了公布: Rylai 是一个女孩子的名字。为什么是女孩子的名字?据说船在起航的时候都会用一个女孩子的名字来命名,具体什么原因,大家可以去搜一下知乎排名第一的答案(我们就不详细说啦)。 Rylai 是 Dota 里面的英雄,叫冰女(水晶室女)。Dota 文化属于 Nervos 亚文化之一,所以未来我们也会用 Dota 里面的英雄命名以后的里程碑。 另外,还有其它的解释: 项目诞生是在寒冬测试网通常会 Freeze 一些 Feature,到主网不会有太大的变化,Freeze 和冰女的气质比较相像更多画面,大家可以联想......当前的 Rylai 中包含了什么?共识(NC-Max)和 P2P 协议虽然现有的项目有成熟的共识和 P2P 协议,但我们还是决定单独将它们做出来。因为对于一个需要经受真实环境残酷考验的项目来说,现有的共识和 P2P 协议并不是那么地完善和适合我们。 Nervos 现在的共识算法叫 NC-Max,由研究员张韧设计。在测试网上线之前,我们最重要的一份工作就是证明 NC-Max 比 Bitcoin 的 Nakamoto Consensus 更好。我们对 Bitcoin 网络和 CKB 网络进行了测量,后续,我们将发布所收集的数据、RFC 以及共识协议的论文。 Rylai 三十多次的迭代过程,也是我们对 NC-Max 以及 P2P 协议参数不断调试的过程。这个参数调优就像是在给一辆汽车做零件的调试(这里请大家自行脑补,周杰伦出演的电影《头文字 D》中,其父亲藤原文太调试汽车的场景),尽可能将发动机、油门、离合器、刹车等部件调到最好的状态。现在我们找到了顺畅运行的参数组合,测试网上线,大家可以来尽情体验了。 CKB-VM我们没有用 EVM,也没有用 WebAssembly,而是基于一个完全由开放的,由社区推动的 RISC-V 指令集,打造了一个新的 CKB-VM,这很符合我们的开源理念。这里有一个很特别的技术设计就是我们把自己验签的算法跑在了 VM 里面,也可以允许用户选择自己认为合适的验证签名的方法。 ...

May 22, 2019 · 1 min · jiezi

RustCon-Asia-实录-Distributed-Actor-System-in-Rust

作者介绍:Zimon Dai,阿里云城市大脑 Rust 开发工程师。 本文根据 Zimon 在首届 RustCon Asia 大会上的演讲整理。 大家好,我今天分享的是我们团队在做的 Distributed Actor System。首先我想说一下这个 Talk 「不是」关于哪些内容的,因为很多人看到这个标题的时候可能会有一些误解。 <center>图 1</center> 第一点,我们不会详细讲一个完整的 Actor System 是怎么实现的,因为 Actor System 有一个很完善的标准,比如说像 Java 的 Akka, Rust 的 Actix 这些都是很成熟的库,在这里讲没有特别大的意义。第二,我们也不会去跟别的流行的 Rust 的 Actor System 做比较和竞争。可能很多人做 Rust 开发的一个原因是 Rust 写的服务器在 Techpower 的 benchmark 上排在很前面,比如微软开发的 Actix,我们觉得 Actix 确实写的很好,而我们也没有必要自己搞一套 Actix。第三,我们不会介绍具体的功能,因为这个库现在并没有开源,但这也是我们今年的计划。 这个 Talk 主要会讲下面几个方向(如图 2),就是我们在做一个 Actor System 或者大家在用 Actor System 类似想法去实现一个东西的时候,会遇到的一些常见的问题。 <center>图 2</center> 首先我会讲一讲 Compilation-stable 的 TypeId 和 Proc macros,然后分享一个目前还没有 Stable 的 Rust Feature,叫做 Specialization, 最后我们会介绍怎么做一个基于 Tick 的 Actor System,如果你是做游戏开发或者有前端背景的话会比较了解 Tick 这个概念,比如做游戏的话,有 frame rate,你要做 60 帧,每帧大概就是 16 毫秒,大概这样是一个 Tick;前端的每一个 Interval 有一个固定的时长,比如说 5 毫秒,这就是一个 Tick。 ...

May 14, 2019 · 4 min · jiezi

Rust-Rc-方法整理

ref: alloc::rc::Rc - Rust <!-- toc --> 方法 newpintry_unwrapinto_rawfrom_rawdowngradeweak_countstrong_countget_mutptr_eqmake_mutdowncast<!-- tocstop --> std::rc::Rc 是单线程引用计数指针。'RC' 代表 'Reference Counted'。翻阅 module-level-documentation 查看更多信息Rc 的固有方法都是关联函数,这意味在使用应该是用类似 Rc::get_mut(&mut value) 而不是 value.get_mut() 的方式调用。这可以避免与其包含的类型方法冲突。 方法newpub fn new(value: T) -> Rc<T> 构造一个 Rc<T> 例子 use std::rc::Rc;let five = Rc::new(5);pinpub fn pin(value: T) -> Pin<Rc<T>> 构建一个新的 Pin<Rc<T>>。如果 T 没有实现 Unpin,那么 value 将会固定在内存中不可移动。 try_unwrappub fn try_unwrap(this: Self) -> Result<T, Self> 如果 Rc 有且只有1个强引用,则返回包含的值,否则返回 Err<T>。不管 Rc 有多少弱引用,只要符合上述条件,该函数都将成功。 use std::rc::Rc;fn main() { let x = Rc::new(3); assert_eq!(Rc::try_unwrap(x), Ok(3)); let x = Rc::new(4); let _y = Rc::clone(&x); // 调用 clone 增强强引用 assert_eq!(*Rc::try_unwrap(x).unwrap_err(), 4); // Rc::try_unwrap(x) 返回 Err(4)}into_rawpub fn into_raw(this: Self) -> *const T ...

May 12, 2019 · 2 min · jiezi

Rustweekly20190509

????????>欢迎参与Rust中文:Rust-weekly 参与入口,本文同步于Rust-weekly-2019-05-09新闻Actix系列在最新的Web框架性能排行榜实现所有选项屠榜The RustBridge Roadmap for 2019This week in Amethyst 16A final proposal for await syntaxThis Week in Rust 285文章用Wasmer进行插件开发1 - Rust中文阅读理解 Rust 中的 Closure试试看 - 搭建自己的 Rust Cargo Crate 反向代理源RUST语言在Windows上的编译安装(GCC ABI)Windows Linux子系统使用rustup重新安装RustRust parallelism for non-C/C++ developersExtending Python with RustRust: How to build a Docker image with private Cargo dependenciesExplained: Futures in Rust for Web Development用Rust写脚本语言 (一):一切存在皆对象(二):垃圾收集器(三):作用域与栈帧(四):插播一则通知和一个脑洞(五):方法对象(六):休养生息(七):迈向多线程的准备——初级阶段从零开始写 OS (0) —— 随便聊聊(1) —— 独立式可执行程序(2) —— 最小化内核(3) —— 格式化输出(4) —— Trap(4-1) —— 中断跳转(5) —— 时钟中断(6) —— 页表简介(7) —— 内存分配(8) —— 创建页表(9) —— 内核线程Kubernetes operators in rustUsing Rust for Gamedev6 useful Rust macros that you might not have seen beforeWorld's First Private Cargo Registry w/ Cloudsmith + Rust如何理解 rust 中的 Sync、Send?[[译] Rust如何解决依赖地狱](https://segmentfault.com/a/11...Compile-time coprocessor codegen, with Rust macrosRust Patterns: Enums Instead Of BooleansHow to build sitemap for images with RustCross-platform RustWriting your first WebAssembly ProjectHow to use datas to build sitemap with Rust Diesel2D Graphics on Modern GPUHawk-Rust Series: Kafka with RustHAWK-Rust Series: Automate Infrastructure using TerraformHawk: Image Recognition Application using Rust and AWS ServicesWebAssembly, Rust, and Edge ComputingAnother Way of Creating struct Instances in RustI underestimated the dangers of Rust FFICratesvtext - NLP in Rust with Python bindingsmicroamp -A (micro) framework for building bare-metal AMP (Asymmetric Multi-Processing) applications ...

May 10, 2019 · 2 min · jiezi

miniserve-简单美观的文件服务器

如果想建立一个简单静态文件或目录服务器,通常可以用 Python 实现,而且非常简单 # Python 2python -m SimpleHTTPServer <port># Python 3python3 -m http.server <port>一般情况下,这就够用了,但如果这样的服务器在浏览器提供的界面有些简陋,而且不提供认证服务。更复杂的实现方法是使用 Nginx,但 Nginx 的配置相对繁琐,这里推荐一个使用 Rust 基于 Actix框架实现静态文件或文件夹服务器 miniserve,demo如下 除了更加漂亮的界面和基本用户认证外 miniserve 还支持如下功能 将当前文件夹压缩后下载界面上传文件(可配置)支持监听多网卡自动更改 MIME超级快(powered by Rust and Actix)下载在发行版界面找到操作系统对应的版本,文件很小,最大的 osx 也仅有 3.2MB。 Linuxsudo curl -L https://github.com/svenstaro/miniserve/releases/download/v0.4.1/miniserve-linux-x86_64 -o /usr/local/bin/miniservesudo chmod +x /usr/local/bin/miniserveOSXsudo curl -L https://github.com/svenstaro/miniserve/releases/download/v0.4.1/miniserve-osx-x86_64 -o /usr/local/bin/miniservesudo chmod +x /usr/local/bin/miniserveWindowswindows 下载好 exe 文件可直接运行 Cargo如果电脑上安装了 Rust 和 Cargo,也可以通过 Cargo 安装,但由于 miniserve仅支持 nightly channel,所以你得先切换到 nightly channel rustup add toolchain nightlyrustup default nightlycargo install miniserveDockerminiserve 在 docker hub 上的镜像名为 svenstaro/miniserve ...

May 1, 2019 · 2 min · jiezi

译-Rust如何解决依赖地狱

每隔一段时间我就会参与一个关于依赖管理和版本的对话,通常是在工作中,其中会出现“依赖地狱”的主题。如果你对这个术语不熟悉,那么我建议你查一下。简要总结可能是:“处理应用程序依赖版本和依赖冲突所带来的挫败感”。带着这个,让我们先获得关于依赖解析的一些技术。 问题在讨论包应该具有哪种依赖关系以及哪些依赖关系可能导致问题时,本主题通常会进入讨论。作为一个真实的例子,在 Widen Enterprises,我们有一个内部的,可重用的Java框架,它由几个软件包组成,为我们提供了创建许多内部服务的基础(如果你愿意的话,微服务)。这很好,但是如果你想创建一个依赖于框架中某些东西的可重用共享代码库呢?如果你尝试在应用程序中使用这样的库,最终可能会得到如下依赖关系图: <!--more--> 就像在这个例子中一样,每当你试图在服务中使用库时,你的服务和库很可能依赖于不同版本的框架,这就是“依赖地狱”的开始。 现在,在这一点上,一个好的开发平台将为你提供以下两种选择的组合: 使构建失败并警告我们framework版本21.1.1和21.2.0相互冲突。使用语义版本控制允许包定义与其兼容的 一系列 版本。如果幸运的话,两个软件包都兼容的版本集是非空的,你最终可以在应用程序中自动使用其中一个版本。这两个看起来都合理,对吧?如果两个软件包确实彼此不兼容,那么我们根本无法在不修改其中一个的情况下将它们一起使用。这是一个艰难的情况,但替代方案往往更糟糕。事实上,Java是不该学习的一个很好的例子: 默认行为是允许将依赖项的多个版本添加到类路径(Java的定位类的方式)。当应用程序需要库中的类时,实际使用哪个版本?在实践中,类的加载顺序因环境而异,甚至以非确定的方式运行,因此你实际上不知道将使用哪一个。哎呀!我们在Widen使用的另一个选择是强制版本对齐。这类似于之前的第二个合理选择,在Java中,依赖关系无法表达兼容性范围,因此我们只选择较新的可能依赖项并祈祷它仍然有效。在前面显示的依赖关系图示例中,我们将强制app升级到framework 21.2.0。这看起来像是一个双输的情况,所以你可以想象,这对添加依赖项非常不利,并且使之成为一个事实上的策略,除了实际的应用程序之外什么都不允许依赖我们的核心框架。 Rust的解决方案在进行这些讨论时,我会经常提到这是一个不适用于所有语言的问题,作为一个例子,Rust“解决”了这个问题。我常常拿Rust如何解决世界上所有的问题开玩笑,但在那里通常有一个真实的核心。因此,当我说Rust“解决”了这个问题以及它是如何工作的时候,让我们深入了解一下我的意思。 Rust的解决方案涉及相当多的动人的部分,但它基本上归结为挑战我们在此之前做出的核心假设: 最终应用程序中只应存在任何给定包的一个版本。Rust挑战了这一点,以便重构问题,看看是否有一个在依赖地狱之外更好的解决方案。Rust平台主要有两个功能可以协同工作,为解决这些依赖问题提供基础,现在我们将分别研究并看看最终结果是怎样的。 Cargo和Crates难题的第一部分当然是Cargo,Rust官方依赖管理器。Cargo类似于NPM或Maven之类的工具,并且有一些有趣的功能使它成为一个真正高质量的依赖管理器(这里我最喜欢的是Composer,一个非常精心设计的PHP依赖管理器)。Cargo负责下载项目依赖的Rust库,称为crates,并协调调用Rust编译器以获得最终结果。 请注意,crates是编译器中的第一类构造。这在以后很重要。 与NPM和Composer一样,Cargo允许你根据语义版本控制的兼容性规则指定项目兼容的一系列依赖项版本。这允许你描述与你的代码兼容(或可能)兼容的一个或多个版本。例如,我可能会添加 [dependencies]log = "0.4.*"到Cargo.toml文件,表明我的代码适用于0.4系列中log包的任何补丁版本。也许在最终的应用程序中,我们得到了这个依赖树 因为在my-project中我声明了与log版本0.4.*的兼容性,我们可以安全地为log选择版本0.4.4,因为它满足所有要求。(如果log包遵循语义版本控制的原则,这个原则对于已发布的库而言并不总是如此,那么我们可以确信这个发布不包括任何会破坏我们代码的重大更改。)你可以在Cargo文档中找到一个更好地解释版本范围以及它们如何应用于Cargo。 太棒了,所以我们可以选择满足每个项目版本要求的最新版本,而不是选择避开遇到版本冲突或只是选择更新的版本并祈祷。但是,如果我们遇到无法解决的问题,例如: 没有可以选择满足所有要求的log版本!我们接下来做什么? 名字修饰为了回答这个问题,我们需要讨论名字修饰。一般来说,名字修饰是一些编译器用于各种语言的过程,它将符号名称作为输入,并生成一个更简单的字符串作为输出,可用于在链接时消除类似命名符号的歧义。例如,Rust允许你在不同模块之间重用标识符: mod en { fn greet() { println!("Hello"); }}mod es { fn greet() { println!("Hola"); }}这里我们有两个不同的函数,名为greet(),但当然这很好,因为它们在不同的模块中。这很方便,但通常应用程序二进制格式没有模块的概念;相反,所有符号都存在于单个全局命名空间中,非常类似于C中的名称。由于greet()在最终二进制文件中不能显示两次,因此编译器可能使用比源代码更明确的名称。例如: en::greet()成为en__greetes::greet()成为es__greet问题解决了!只要我们确保这个名字修饰方案是确定性的并且在编译期间到处使用,代码就会知道如何获得正确的函数。 现在这不是一个完全完整的名字修饰方案,因为我们还没有考虑很多其他的东西,比如泛型类型参数,重载等等。此功能也不是Rust独有的,并且确实在C++和Fortran等语言中使用了很长时间。 名字修饰如何帮助Rust解决依赖地狱?这一切都在Rust的名字管理体系中,这似乎在我所研究的语言中相当独特。那么让我们来看看? 在Rust编译器中查找名字修饰的代码很简单;它位于一个名为symbol_names.rs的文件中。如果你想学习更多内容,我建议你阅读这个文件中的注释,但我会包括重点。似乎有四个基本组件包含在一个修饰符号名称中: 符号的完全限定名称。通用类型参数。包含符号的crate的名称。(还记得crates在编译器中是一流的吗?)可以通过命令行传入的任意“歧义消除器(disambiguator)”字符串。使用Cargo时,Cargo本身会将“歧义消除器”提供给编译器,所以让我们看一下compilation_files.rs包含的内容: 包名字包源包版本启用编译时功能一堆其他的东西这个复杂系统的最终结果是,即使是不同版本的crate中的相同功能也具有不同的修饰符号名称,因此只要每个组件知道要调用的函数版本,就可以在单个应用程序中共存。 合在一起现在回到我们之前的“无法解决的”依赖图: 借助依赖范围的强大功能,以及Cargo和Rust编译器协同工作,我们现在可以通过在我们的应用程序中包含log 0.5.0和log 0.4.4来实际解决此依赖关系图。app内部使用log的任何代码都将被编译以达到从0.5.0版生成的符号,而my-project中的代码将使用为0.4.4版生成的符号。 现在我们看到了大局,这实际上看起来非常直观,并解决了一大堆依赖问题,这些问题会困扰其他语言的用户。这个解决方案并不完美: 由于不同版本生成不同的唯一标识符,因此我们无法在库的不同版本之间传递对象。例如,我们无法创建一个log 0.5.0的LogLevel并将其传递给my-project使用,因为它期望LogLevel来自log 0.4.4,并且它们必须被视为单独的类型。对于库的每个实例,任何静态变量或全局状态都将被复制,如果没有一些特殊方法,它们就无法通信。我们的二进制大小必然会因为我们应用程序中包含的库的每个实例而增加。由于这些缺点,Cargo仅在需要时才采用这种技术来解决依赖图。 为了解决一般用例,这些似乎值得为Rust做出权衡,但对于其他语言,采用这样的东西可能会更加困难。以Java为例,Java严重依赖于静态字段和全局状态,因此简单地大规模采用Rust的方法肯定会增加破坏代码的次数,而Rust则将全局状态限制在最低限度。这种设计也没有对在运行时或反射时加载任意库进行说明,这两者都是许多其他语言提供的流行功能。 结论Rust在编译和打包方面的精心设计以(主要)无痛依赖管理的形式带来红利,这通常消除了可能成为开发人员在其他语言中最糟糕的噩梦的整类问题。当我第一次开始玩Rust的时候,我当然很喜欢我所看到的,深入了解内部,看到宏大的架构,周到的设计,以及合理的权衡取舍对我来说更令人印象深刻。这只是其中的一个例子。 即使你没有使用Rust,希望这会让你对依赖管理器,编译器以及他们必须解决的棘手问题给予新的重视。(虽然我鼓励你至少尝试一下Rust,当然......) GitHub repo: qiwihui/blogFollow me: @qiwihui ...

April 30, 2019 · 1 min · jiezi

首届-RustCon-Asia-圆满落幕Lets-Rust-the-World

4 月 23 日,为期 4 天的 RustCon Asia 在北京圆满落幕,300 余位来自中国、美国、加拿大、德国、俄罗斯、印度、澳大利亚等国家和地区的 Rust 爱好者参加了本次大会。作为 Rust 亚洲社区首次「大型网友面基 Party」,本届大会召集了 20 余位海内外顶尖 Rust 开发者讲师,为大家带来一天半节奏紧凑的分享和两天 Workshop 实操辅导,内容包括 Rust 在分布式数据存储、安全领域、搜索引擎、嵌入式 IoT、图像处理等等跨行业、跨领域的应用实践。 会后我们收到了很多小伙伴们的反馈,大家纷纷表示干货密集,诚意满满,而且通过 Workshop 和很多大神面对面讨论交流,非常受益。 “这次参加 RustCon Asia 非常直观的感受到了 Rust 开发的应用早已渗透到我们的生活中,以前跟朋友推广 Rust 的时候总是说不出什么杀手级应用,现在总算有些可以说了!”来自宝岛台湾的 Weihang Lo 表示这次大会有两个热情的东道主,更有许多赞助好伙伴,让整个大会品质非常高,“参会的小伙伴们热情、不藏私,让人看到了中国技术发展与交流真的非常蓬勃,大家都是 Learning by Sharing,整体技术能力也很高。台上的所有讲师,每一个都技术了得,面对很多问题都能从容应答,技术人员若能如此,夫复何求?”从新加坡赶来参会的 Alan 说:“这个大会非常专业,我也认识了很多朋友。 我最喜欢的 Talk 是 Zimon Dai 的,他给我们这些做 APP 的人带来很多新的看法,来自俄罗斯讲师 Ilya Baryshnikov 分享了自己如何在工作中使用 Rust,让我受到了启发,整个会场气氛很热闹,交流很顺畅。我参加了好几个 Workshop,Nick 的 Worskhop 对我有很大帮助,Parity 的 Workshop 让我知道怎么创造一个区块链……” 看到大家能够学到干货受到启发、与老朋友聚会同时又结识了新朋友,作为本届 RustCon Asia 的主办方之一,我们感到非常欣慰。其实自从使用 Rust 构建分布式 Key-Value 数据库 TiKV 以来,PingCAP 与 Rust 社区就保持了非常紧密的联系,非常有幸见证了 Rust 亚洲社区成熟、壮大。同时也很欣喜的看到 Rust 在各个行业和领域应用的蓬勃之势,希望今后能看到 Rust 在各行各业遍地开花,Let's Rust the World! ...

April 26, 2019 · 1 min · jiezi

TiKV-源码解析六raftrs-日志复制过程分析

作者:屈鹏 在 《TiKV 源码解析(二)raft-rs proposal 示例情景分析》 中,我们主要介绍了 raft-rs 的基本 API 使用,其中,与应用程序进行交互的主要 API 是: RawNode::propose 发起一次新的提交,尝试在 Raft 日志中追加一个新项;RawNode::ready_since 从 Raft 节点中获取最近的更新,包括新近追加的日志、新近确认的日志,以及需要给其他节点发送的消息等;在将一个 Ready 中的所有更新处理完毕之后,使用 RawNode::advance 在这个 Raft 节点中将这个 Ready 标记为完成状态。熟悉了以上 3 个 API,用户就可以写出基本的基于 Raft 的分布式应用的框架了,而 Raft 协议中将写入同步到多个副本中的任务,则由 raft-rs 库本身的内部实现来完成,无须应用程序进行额外干预。本文将对数据冗余复制的过程进行详细展开,特别是关于 snapshot 及流量控制的机制,帮助读者更深刻地理解 Raft 的原理。 一般 MsgAppend 及 MsgAppendResponse 的处理在 Raft leader 上,应用程序通过 RawNode::propose 发起的写入会被处理成一条 MsgPropose 类型的消息,然后调用 Raft::append_entry 和 Raft::bcast_append 将消息中的数据追加到 Raft 日志中并广播到其他副本上。整体流程如伪代码所示: fn Raft::step_leader(&mut self, mut m: Message) -> Result<()> { if m.get_msg_type() == MessageType::MsgPropose { // Propose with an empty entry list is not allowed. assert!(!m.get_entries().is_empty()); self.append_entry(&mut m.mut_entries()); self.bcast_append(); }}这段代码中 append_entry 的参数是一个可变引用,这是因为在 append_entry 函数中会为每一个 Entry 赋予正确的 term 和 index。term 由选举产生,在一个 Raft 系统中,每选举出一个新的 Leader,便会产生一个更高的 term。而 index 则是 Entry 在 Raft 日志中的下标。Entry 需要带上 term 和 index 的原因是,在其他副本上的 Raft 日志是可能跟 Leader 不同的,例如一个旧 Leader 在相同的位置(即 Raft 日志中具有相同 index 的地方)广播了一条过期的 Entry,那么当其他副本收到了重叠的、但是具有更高 term 的消息时,便可以用它们替换旧的消息,以便达成与最新的 Leader 一致的状态。 ...

April 25, 2019 · 3 min · jiezi

Tensorflow Rust实战上篇

机器学习的一个方向是能够将它用于照片中的对象识别。这包括能够挑选动物,建筑物甚至人脸等特征。本文将引导您使用一些现有模型来使用rust和tensorflow完成人脸检测。我们将使用一个名为mtcnn的预训练模型进行人脸检测(注意:训练新模型不是我们在本文中关注的内容)。 挑战我们想要读取照片,检测到人脸,然后返回带有绘制边界框的图像。换句话说,我们想转换它(图片使用RustFest的许可,由FionaCastiñeira拍摄): Tensorflow and MTCNN最初的MTCNN模型是使用Caffe编写的,但幸运的是mtcnn有许多tensorflow python实现。我将选择tf-mtcnn,因为它是直接转换为单个图形模型文件。 首先,我们要添加tensorflow rust作为依赖。从Cargo.toml开始: [package]name = "mtcnn"version = "0.1.0"edition = "2018"[dependencies]tensorflow = "0.12.0"我们要做的是加载一个Graph,它是预先训练好的MTCNN,并运行一个会话。要点是Graph是用于计算的模型,Session是Graph的一次运行。有关这些概念的更多信息可以在这里找到。我喜欢将Graph视为大桶中的人造大脑,用途只是在您输入和输出时获得一些很棒的图像。 因此,让我们首先抓住现有的mtcnn.pb模型并尝试加载它。 Tensorflow图以protobuf格式序列化,可以使用Graph::import_graph_def加载。 use std::error::Error;use tensorflow::Graph;use tensorflow::ImportGraphDefOptions;fn main() -> Result<(), Box<dyn Error>> { //First, we load up the graph as a byte array let model = include_bytes!("mtcnn.pb"); //Then we create a tensorflow graph from the model let mut graph = Graph::new(); graph.import_graph_def(&*model, &ImportGraphDefOptions::new())? Ok(())}跑cargo run命令,我们应该看到没有任何错误: $ cargo run Compiling mtcnn v0.1.0 (~/mtcnn) Finished dev [unoptimized + debuginfo] target(s) in 0.89s Running `target/debug/mtcnn`太棒了!看起来我们可以加载此图像! ...

April 21, 2019 · 3 min · jiezi

BlockLang - 软件拼装工厂

转眼间,做业务系统的软件开发已有十个年头,从刚开始的激情满满,到周而复始地一个接一个的做项目,虽然竭尽全力将一些常用的代码或模式封装到框架中,但依然感觉到了无尽的重复,而正是这无尽的重复在逐渐的吞噬着我的工作热情。我意识到,虽然我热爱软件研发,但目前的业务系统软件研发模式,让大家深陷在沼泽中,逐渐没有了生活。做过一件事情之后,或者踩过一个坑后,如果能够将可复用的代码提取到框架中,供更多的人去调用,而不是重复发明轮子;如果能够将我们在项目中已获得的经验固化为平台中的一个能力,然后为平台用户赋能,这样每个人的起点都是站在巨人的肩膀上。倘若能够形成这样的机制,则我们的工作内容中无聊和冗余的重复将大大减少,并且会极大的提供工作质量和效率,出现全新的软件研发模式。这样技术前提下的软件研发模式,也能极大的促进业务系统的高速发展,因为开发一个软件的周期大大降低,甚至能够做到实时反馈,业务专家不用再在提出一个认为很简单的需求,却在一个月之后看到与预期相差甚远的功能;而是可以边提需求、边开发、边确认。于是,我辞掉了工作,着手搭建这样的一个平台,并完全开源,并殷切的期望能吸纳各方面的人才参与,形成一个活跃的社区。平台名字叫 BlockLang, 也就是块语言,寓意使用这个“块状的语言”像摆积木一样拼装出业务系统。BlockLang 相信“每个人都可按照自己的需求,拼装出称心的软件”。BlockLang 致力于打造一朵“百花齐放、百鸟争鸣”的软件云,实现软件定义软件。告别传统的业务系统开发模式,人人都能高效率的拼装出高质量的业务系统。BlockLang 是一个开源项目源码托管在 https://github.com/blocklang 和 https://gitee.com/blocklang演示站点在 https://blocklang.com诚邀志同道合的编程手艺人加入(QQ群 619312757),一起开启业务系统研发新模式。

April 20, 2019 · 1 min · jiezi

rust读书笔记基础1

rust名词和cargorust中的名词代码包:crates原始标识符:没理解rust中宏和函数的区别?rust的宏和函数调用时候形式的区别,宏带!而函数调用不带;rust中的cargocargo new 项目名:创建新的项目,项目名成小写下划线链接cargo build:构建项目,生成编译后的可执行文件,用于开发cargo run:编译和运行项目cargo check:确保项目可以编译cargo build –release:优化编译项目,用于测试rust中的基础知识变量Rust 代码中的函数和变量名使用 snake case 规范风格。变量默认不可变在变量前加上mut标识,标识可变变量变量和常量的区别,变量使用let修饰,常量使用const,并且不能使用mut修饰,常量不能是运行时计算出来的值隐藏:隐藏是多次使用let修饰同一个名字的变量,后面的变量会覆盖之前的变量;隐藏可以改变之前变量的类型,mut则不可以。(关注一下垃圾回收对覆盖的处理)基本类型整型标量长度有符号无符号8-biti8u816-biti16u1632-biti32u3264-biti64u64archisizeusize其他标量长度类型描述32-bitf32单精度浮点型64-bitf64双精度浮点型32-bitchar字符型8-bitbool布尔型复合类型元组:类型可以不同,解构访问或者点访问数组:类型相,长度固定,栈上分配函数函数定义//fn+函数名+(参数)+ -> + 返回值类型 + {函数体}fn function_name (parameters) -> type_of_return { body;}函数签名中,必须声明每个参数的类型;返回值可以有多个,使用元组;函数调用//函数名+() function_name ();控制流if和其他的语言类似循环:loop + breakfn main() { let mut counter = 0; let result = loop { counter += 1; if counter == 10 { break counter * 2; } }; assert_eq!(result, 20);}forfn main() { let a = [10, 20, 30, 40, 50]; for element in a.iter() { println!(“the value is: {}”, element); }}whilefn main() { let a = [10, 20, 30, 40, 50]; let mut index = 0; while index < 5 { println!(“the value is: {}”, a[index]); index = index + 1; }} ...

April 19, 2019 · 1 min · jiezi

终极讲师介绍:集齐 27 位讲师召唤亚洲首届 Rust 开发者大会!

RustCon Asia 进入倒计时!就在这个周六,将有 300+ 位开发者齐聚北京,参加亚洲最大的 Rust 语言开发者大会 RustCon Asia。此次大会几乎将聚集全部 Rust 中国社区的资深开发者和已在生产环境应用的中国本土的 Rust 项目,以及来自亚洲之外的欧洲、澳洲、北美的顶尖开发者们。大家都约好面基了吗?时间:4 月 20 -23 日大会地点: 北京朝阳区广顺南大街 8 号 · 北京望京凯悦大酒店Workshop 地点: 北京朝阳区大望京科技商务园区浦项中心A座之前我们发布了「大神面基指南(一)」介绍了 8 位明星讲师,今天我们继续为大家介绍 19 位重量级讲师和他们的议题,快来看看有没有你感兴趣的吧!ALEX:资深软件工程师,企业独立咨询师,技术书籍译者、作者Alex 将会在 RustCon Asia 进行两场主题分享。在 talk 环节,Alex 将会为大家介绍 Rust 基础,主题为「How to learn Rust efficiently」,听完后想必会对“Rust 学习曲线高”的真正原因有所了解。在 workshop 环节,Alex 将会带来「Rapid Development of RESTful microservices using actix-web and diesel」主题分享,在该分享中,会以 TodoList 为例,讲讲使用 Actix-web 和 Diesel 来实现微服务接口服务。ROBOTXY:阿里妈妈,Rust 开发工程师Robotxy 将跟大家分享当前他正在做的手淘首焦混竞项目,在高性能、高稳定性的需求场景下使用 Rust 的实践经验。该项目到目前为止已经稳定运营两年多,还经历过双十一的考验。在这个过程中,遇到了很多的问题, 必然是大家学习借鉴 Rust 在生产环境中的好案例。ZIMON DAI:阿里云城市大脑,Rust 开发工程师本次大会上,将会分享 Actor 系统。Actor 系统是 Rust 目前流行的应用方向(如 actix)。Zimon 用 Rust 编写了分布式的 Actor 系统框架 UPS,并在此基础上开发大规模的运算系统。这次大会上,他将会着重分享设计分布式的 Actor 系统中的几个关键技术问题。听完这个 talk,想必大家也可以轻松编写出符合自己需要的简单 Actor 系统。而且,据说很快开源。WAYSLOG:Bilibili 高级中间件开发工程师Wayslog 将会在本次 RustCon Asia 大会带来两个主题分享。在 talk 环节,他将会分享「Rust at Bilibili 」,介绍 Bilibili 在应用 Rust 过程中遇到的问题和处理方法。据说,只要胆子大, 他可以手把手教你写 Rust。在 workshop 环节,Wayslog 则会带大家一起做一个简单的 RESP parser。OLIVIA HUGGER:RustBridge 组织者在 workshop 中,Olivia 将会手把手教大家编写 Rust。第一部分重点介绍 Rust 语言的语法和语义,并将 Rust 与其他语言的编程概念联系起来,特别是流行的脚本语言,如 JavaScript 或 Ruby。然后还会有一个交互式演示, 以及使用 Rustling 进行指导和自我指导练习。丁羽:北京大学计算机博士,X-Lab 高级安全研究员本次 RustCon Asia,丁羽会和孙茗珅会在 Workshop 上,围绕「Build a Secure and Trusted Framework in Rust」深入介绍用 Rust 构建一个安全可信框架,一步步引导大家学习和讨论。此次 X-Lab Workshop 将会在两个方面带大家一起玩 Rust:SGX 和 Trustzone。丁羽和孙茗珅老师会就可信任计算理论和硬件辅助信任执行引擎做介绍,然后就是 hands on 时间!带大家在 Rust+SGX 和 Rust+Trustzone 编程实践和最后的讨论!有兴趣的同学请做好功课哦。Ilya Baryshnikov:Rust 开发工程师在 talk 环节,Ilya 将介绍 WebAssembly 提供几个在 React + three.js app 中的「heavy computations」实例,并且比较 JS 和 Rust 的性能。然后,Ilya 将会分享 Rust 和 WebAssembly 运用到 App 中的经验。带大家一起讨论 wasm-bindgen 库将会如何帮助你与 JS 世界通信,并且减少引用。在 workshop 环节,Ilya 将会讨论更多关于 Rust 和 WebAssembly 的细节,并且就两个主题:moving computations to WASM 和 DOM maniputations 深入交流。Gautam Dhameja:作家,Rust 开发工程师本次大会,Gautam 将会为我们带来主题为「Building a blockchain using Rust with Parity Substrate」的 Workshop。在这个 workshop 中,Gautam 会教大家学习使用 Rust 来构建一个自定义、高效和模块化的区块链,包括 Substrate 框架介绍,如何使用其进行开发以及案例演示。XIDORN QUAN:Mozillian,Gecko developer在这次 RustCon Asia 大会上,Xidorn 将会给大家带来「Re: Zero-writing a custom derive」的主题分享。自定义派生代码对于新入门的开发者来说是一个挑战,但不得不说是一个非常有用的工具,可以让人们在完成更多事情的同时编写更少的代码,在 Servo 样式系统中被广泛使用。这次主题分享将简要介绍如何在 Servo 样式系统中使用自定义派生代码,并描述如何从头开发自定义派生代码。HAWKINGREI:Bilibili 中间件开发工程师在主题演讲环节,Hawkingrei 将会分享 Bilibili 在应用 Rust 过程中遇到的问题和处理方法。在 workshop 环节,他将和 Wayslog 老师一起学手把手带大家学习 Rust ffi,从入门到精通,使你可以 bindgen 任何 c/c++ 库,同时可以对 c/c++ 进行一定的包装。DRIFTLUO:Rust 开发工程师P2P 是区块链网络的基础,是不应被忽视的部分。这次大会上,Driftluo 将介绍 P2P 项目库,从初衷到实现的过程,以及未来的发展、可能遇到的障碍。宁志伟:秘猿科技研究员,编程语言爱好者在本次大会中,志伟将为我们带来「Rust at Cryptape」的 Workshop,同时,志伟欢迎所有对 Rust 感兴趣伙伴的参与。如果您对区块链感兴趣,也可以在 workshop 过程中,和志伟进行讨论。另外,志伟会和 Ana 一起主持此次大会哦!屈鹏:TiKV 研发工程师这次 RustCon Asia 大会,屈鹏将带来的主题为「Futures in TiKV」演讲。Rust 凭借出色的编译期内存管理及对 C 的无缝兼容成为系统编程的最佳候选者。屈鹏所在的团队使用 Rust 从零开始研发了整个 TiKV,其中大量的并行处理逻辑都基于 futures-rs 库。本次演讲中,屈鹏将会介绍 futures-rs 在 TiKV 中的基本用法,以及如何自行构建类似的并发模型等较高级的主题。唐威:Parity 开发工程师,SputnikVM 和 Rux microkernel 的作者,ETC 团队的 Rust 开发者在本次演讲中,唐威将会和我们分享他实现 libsecp256k1 的经验。libsecp256k1 是一个在 no_std 环境中运行的纯 Rust 代码库,它能提供完整的 secp256k1 签名和验证功能。WISH:TiKV 研发工程师SHIRLY:TiKV 核心开发工程师Wish 将会和 Shirly一起带来「Integrate rust-prometheus into your application」主题演讲。而在 workshop 中,他们将以 Rust 编写的简单 Web 服务器为例,教开发者如何使用 Rust-prometheus 连续收集应用程序的指标。Ana(hoverbear):TiKV 高级数据库工程师,终身开源贡献者在 RustCon Asia,Ana 将与秘猿科技研究员宁志伟一起主持这次 Rust 社区大会。Ana 是一个对开源社区、技术、教育充满热情的人,Ana 也很爱交朋友,记得在 RustCon Asia 寻找 Ana,并且成为好友吧~唐刘:PingCAP 首席架构师大会的第一位开场讲师就是唐刘老师,据说他会比较含蓄收敛地说一下 Rust in PingCAP,TiKV 从无到有再到逐渐壮大的过程,包括参与贡献的社区伙伴们和过去的面基活动,以及当前 PingCAP 正在做的系列课程等等,给大家暖暖场。吕国宁:RubyChina 的管理员,Ruby Conf China 主办者这一次,作为 RustCon Asia 的发起者和组织者之一,想必 Daniel 也能感受到当年发起 Ruby Conf China 那一份美好的触动。这回在 Rust 社区大会上 Daniel 将会给大家带来主题为「Why RustCon Asia」开场演讲,从一个开源社区长期贡献者和管理者的角度来看当前的 Rust 社区。让我们一起期待 Daniel 会给大家带来怎样的时代感悟吧~此次 RustCon Asia 大会为期四天,包括 20 日全天和 21 日上午的主题演讲和 22-23 日的多个主题 workshop 环节。其中主题演讲讲师来自于国内外资深 Rust 开发者和社区活跃贡献者;workshop 主题将覆盖到 Rust 开发入门和成熟技术栈或产品的实战操作和演示。大会马上到来,小伙伴们敬请期待吧~活动时间:4 月 20-23 日大会地点:北京朝阳区广顺南大街 8 号北京望京凯悦酒店Workshop 地点:北京朝阳区大望京科技商务园区浦项中心A座目前 RustCon Asia 还有少量余票,点击【这里】购买。大会官网:https://rustcon.asia/Twitter @RustConAsia ...

April 19, 2019 · 2 min · jiezi

rust使用vscode调试环境

需安装vscode插件 RLS(vscode搜索插件rls) 、 lldb(vscode搜索插件codelldb)lldb的launch.json配置内容"version": “0.2.0”, “configurations”: [ { “name”: “rust”, // 配置名称,将会在调试配置下拉列表中显示 “type”: “lldb”, // 调试器类型:Windows表示器使用cppvsdbg;GDB和LLDB使用cppdbg。该值自动生成 “request”: “launch”, // 调试方式 “program”: “${workspaceRoot}/target/debug/helloworld”, // 要调试的程序(完整路径,支持相对路径) “args”: [], // 传递给上面程序的参数,没有参数留空即可 “stopAtEntry”: false, // 是否停在程序入口点(即停在main函数开始)(目前为不停下) “cwd”: “${workspaceRoot}”, // 调试程序时的工作目录 “environment”: [], “externalConsole”: false, // 调试时是否显示控制台窗口(目前为不显示) //“preLaunchTask”: “build”, //预先执行task.json “MIMode”: “lldb” //MAC下的debug程序 } ]

April 15, 2019 · 1 min · jiezi

【RUST官方语言中文翻译】准备工作--安装

让我们开始Rust的学习之旅!有很多东西需要学习, 但是正如每个旅程都会从某个地方开始。在本章中,我们会讨论:在Linux,macOS,Windows中安装Rust编写一个输出Hello, world!的程序使用cargo,进行Rust的包管理和系统编译安装第一步:安装Rust。我们从rustup–一个用于管理Rust版本和关联工具的命令行工具–下载Rust。你需要连接网络以便进行下载。注意:如果你因为各种原因选择不通过rustup的方式,那么请到Rust官网查看其他方式。接下去的步骤会介绍如何安装最新文档稳步版本的Rust编译器。Rust的稳定性确保了本书中所有的示例都能够在最新版本的Rust中编译通过。由于Rust经常会提升错误信息和警告信息的输出体验,每个版本的编辑结果的输出可能会有轻微不同。换句话说,任何你安装的更新的,稳定版本的Rust,对于本书的示例都会有符合预期的输出。命令行标记在本章节或者在全书的各个地方,我们会展示一些在终端使用的命令。在命令行中展示的内容行都会以$开头。你不需要手动输入$符号,它仅仅表示每个命令行的开始。那些不以$开始的内容行,则表示的是前一个命令的输出。另外,特殊的PowerShell示例则会用>而不是$在Linux和macOS上安装rustup如果你使用Linux或者macOS系统,那么请打开终端并输入如下命令curl https://sh.rustup.rs -sSf | sh该命令会下载一段脚本并开始`rustup 这个工具最新版本的安装过程。安装过程中可能会出现要求你输入密码的提示。如果安装成功,将会出现以下内容:Rust is installed now. Great!如果你愿意,你可以在下载脚本并在运行它之前先检查下脚本内容。整个安装过程会在你下次登录时,自动将Rust添加到你的系统变量PATH里。如果你希望可以不需要重新启动终端,直接开始使用Rust,请在终端中运行如下命令以添加Rust到你的系统变量里:source $HOME/.cargo/env或者,添加以下内容到你到 ~/.bash_profile中:export PATH="$HOME/.cargo/bin:$PATH"除此之外,你需要某种链接器。很可能你的系统已经安装了链接器,不过,如果当你试图编译一个Rust程序出错,并且提示你说链接器没有执行,那就是说你的系统并没有安装链接器,你需要手动安装他。C语言的编译器通常会带有正确的链接器。你可以查看你系统的文档来了解如何安装C的编译器。当然,一些通用的Rust包依赖于C语言代码且往往需要一个C的编译器。因此,你可以现在就安装它。在Windows上安装rustup在windows中,打开https://www.rust-lang.org/too…,按照按照指引进行安装。在安装过程中,你会收到一条消息提示你说你需要安装Visual Studio 2013或更新版本的C++构建工具。最简单的获取该构建工具的方法是安装Build Tools for Visual Studio 2017, 该工具可以在网页中其他工具和框架中获取。本书的剩余部分所展示的命令都能在cmd.exe和PowerShell中使用。如果针对某个场景会有不同,我们会在书中明确指出。更新和卸载当你通过rustup 安装Rust之后,就可以很容易进行Rust的版本更新。打开你的终端,运行如下命令:rustup update卸载Rust和rustup , 请在你的终端中运行以下卸载脚本rustup self uninstall常见问题打开终端并输入如下命令检查你是否正确安装了Rustrustc –version之后,你应该会看到输出带有最新稳定版本的版本号,提交hash值,提交日期,格式如下:rustc x.y.x (abcabcabc yyyy-mm-dd)如果你看到信息,就说明你已经成功安装了Rust。如果你没有看到此信息,Windows环境下,请检查Rust是否在你的%PATH%系统变量中。如果所有步骤都没有问题,但是Rust依然不能正常工作,你可以在很多地方获取帮助。最简单的方式是通过Mibbit访问the #rust IRC channel on irc.mozilla.org 。 在这个地方,你可以通过和其他Rustaceans(我们对于自己的一种戏称)聊天获取帮助。你还可以从the Users forum和Stack Overflow获取帮助。本地文档安装包包含了一份本地文档的拷贝,这样你可以在安装后离线阅读文档。运行rustup doc可以在游览器中打开文档进行游览。在任何时候,如果你不知道如何使用标准库提供的类型或者函数,都可以通过API文档找出对应的使用说明。

April 15, 2019 · 1 min · jiezi

Rust 实战 - 使用套接字联网API(二)

上一节,我们已经实现了一个最小可运行版本。之所以使用Rust而不是C,是因为Rust具备了必要的抽象能力,还能获得跟C差不多的性能。这一节,我们对上一节的代码做必要的封装,顺便还能把unsafe的代码包装成safe的API。我将上一节的源码放到了这里,你可以去查看。还记得上一节,我们把使用到的libc中的函数socket、bind、connect和结构体sockaddr、sockaddr_in、in_addr等,在Rust这边定义了出来。实际上,几乎libc中的函数,libc这个crate都帮我们定义好了。你可以去这里查看。编译器和标准库本身也使用了这个crate,我们也使用这个。首先在Cargo.toml文件的[dependencies]下面加入libc = “0.2”:[dependencies]libc = “0.2"接着在main.rs文件上方加入use libc;,也可以use libc as c;。或者你直接简单粗暴use libc::,并不推荐这样,除非你明确知道你使用的函数来自哪里。并将我们定义的与libc中对用的常量、函数、结构体删除。再添加libc::或c::到我们使用那些常量、结构体、函数的地方。如果你是直接use libc::,除了直接删除那部分代码外,几乎什么都不用做。目前的代码:use std::ffi::c_void;use libc as c;fn main() { use std::io::Error; use std::mem; use std::thread; use std::time::Duration; thread::spawn(|| { // server unsafe { let socket = c::socket(c::AF_INET, c::SOCK_STREAM, c::IPPROTO_TCP); if socket < 0 { panic!(“last OS error: {:?}”, Error::last_os_error()); } let servaddr = c::sockaddr_in { sin_family: c::AF_INET as u16, sin_port: 8080u16.to_be(), sin_addr: c::in_addr { s_addr: u32::from_be_bytes([127, 0, 0, 1]).to_be() }, sin_zero: mem::zeroed() }; let result = c::bind(socket, &servaddr as *const c::sockaddr_in as *const c::sockaddr, mem::size_of_val(&servaddr) as u32); if result < 0 { println!(“last OS error: {:?}”, Error::last_os_error()); c::close(socket); } c::listen(socket, 128); loop { let mut cliaddr: c::sockaddr_storage = mem::zeroed(); let mut len = mem::size_of_val(&cliaddr) as u32; let client_socket = c::accept(socket, &mut cliaddr as *mut c::sockaddr_storage as *mut c::sockaddr, &mut len); if client_socket < 0 { println!(“last OS error: {:?}”, Error::last_os_error()); break; } thread::spawn(move || { loop { let mut buf = [0u8; 64]; let n = c::read(client_socket, &mut buf as *mut _ as *mut c_void, buf.len()); if n <= 0 { break; } println!(”{:?}", String::from_utf8_lossy(&buf[0..n as usize])); let msg = b"Hi, client!"; let n = c::write(client_socket, msg as *const _ as *const c_void, msg.len()); if n <= 0 { break; } } c::close(client_socket); }); } c::close(socket); } }); thread::sleep(Duration::from_secs(1)); // client unsafe { let socket = c::socket(c::AF_INET, c::SOCK_STREAM, c::IPPROTO_TCP); if socket < 0 { panic!(“last OS error: {:?}”, Error::last_os_error()); } let servaddr = c::sockaddr_in { sin_family: c::AF_INET as u16, sin_port: 8080u16.to_be(), sin_addr: c::in_addr { s_addr: u32::from_be_bytes([127, 0, 0, 1]).to_be() }, sin_zero: mem::zeroed() }; let result = c::connect(socket, &servaddr as *const c::sockaddr_in as *const c::sockaddr, mem::size_of_val(&servaddr) as u32); if result < 0 { println!(“last OS error: {:?}”, Error::last_os_error()); c::close(socket); } let msg = b"Hello, server!"; let n = c::write(socket, msg as *const _ as *const c_void, msg.len()); if n <= 0 { println!(“last OS error: {:?}”, Error::last_os_error()); c::close(socket); } let mut buf = [0u8; 64]; let n = c::read(socket, &mut buf as *mut _ as *mut c_void, buf.len()); if n <= 0 { println!(“last OS error: {:?}”, Error::last_os_error()); } println!("{:?}", String::from_utf8_lossy(&buf[0..n as usize])); c::close(socket); }}你编译运行,应该能得到与上一节同样的结果。接下来,我们尝试把上面代码中函数,封装成更具Rust风格的API,除了TCP外,也还要考虑之后把UDP、UNIX域和SCTP也增加进来。同时,我们跟标准库里 net相关的API保持一致的风格。我们暂时不考虑跨平台,只考虑Linux,因此可以大胆的将一些linux独有的API添加进来。UNIX中一切皆文件,套接字也不例外。字节流套接字上的read和write函数所表现出来的行为,不同于通常的文件I/O。字节流套接字上调用read和write输入或输出字节数可能比请求的要少,这个现象的原因在于内核中用于套接字的缓冲区可能已经达到了极限。不过,这并不是我们正真关心的。我们来看看标准库中 File的实现:pub struct File(FileDesc);impl File { … pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) } pub fn write(&self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } pub fn duplicate(&self) -> io::Result<File> { self.0.duplicate().map(File) } …}File 是一个元组结构体,标准库已经实现了read和write,以及duplicate。duplicate很有用,用于复制出一个新的描述符。我们继续看File中"包裹的FileDesc:pub struct FileDesc { fd: c_int,}impl File { … pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { let ret = cvt(unsafe { libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), max_len())) })?; Ok(ret as usize) } pub fn write(&self, buf: &[u8]) -> io::Result<usize> { let ret = cvt(unsafe { libc::write(self.fd, buf.as_ptr() as const c_void, cmp::min(buf.len(), max_len())) })?; Ok(ret as usize) } pub fn set_cloexec(&self) -> io::Result<()> { unsafe { cvt(libc::ioctl(self.fd, libc::FIOCLEX))?; Ok(()) } } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { unsafe { let v = nonblocking as c_int; cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?; Ok(()) } }}这一层应该是到头了,你可以看到,Rust中的File也是直接对libc的封装,不过你不用担心,一开始就提到,Rust 的ABI与C的ABI是兼容的,也就意味着Rust和C互相调用是几乎是零开销的。FileDesc的read和write中的实现,与我们之前对sockfd的read和write基本是一样的。除了read和write外,还有两个很有用的方法set_cloexec和set_nonblocking。我把“依附于”某个类型的函数叫做方法,与普通函数不同的是,依附于某个类型的函数,必须通过它所依附的类型调用。Rust通过这种方式来实现OOP,但是与某些语言的OOP不同的是,Rust的这种实现是零开销的。也就是,你将一些函数依附到某个类型上,并不会对运行时造成额外的开销,这些都在编译时去处理。set_cloexec方法会对描述符设置FD_CLOEXEC。我们经常会碰到需要fork子进程的情况,而且子进程很可能会继续exec新的程序。对描述符设置FD_CLOEXEC,就意味着,我们fork子进程时,父子进程中相同的文件描述符指向系统文件表的同一项,但是,我们如果调用exec执行另一个程序,此时会用全新的程序替换子进程的正文。为了较少不必要的麻烦,我们以后要对打开的描述符设置FD_CLOEXEC,除非遇到特殊情况。set_nonblocking用于将描述符设置为非阻塞模式,如果我们要使用poll、epoll等api的话。既然标准库已经封装好了FileDesc,我想直接使用的,然而FileDesc在标准库之外是不可见的。如果使用File的话,set_cloexec 和 set_nonblocking 还是要我们再写一次,但是File并不是“我自己”的类型,我没法直接给File附加方法,为此还需要一个额外的Tarit或者用一个“我自己”的类型,去包裹它。挺绕的。那既然这样,我们还是自己来吧。不过我们已经有了参考,可以将标准库里的FileDecs直接复制出来,然后去掉与Linux无关的代码,当然你也可以自由发挥一下。要注意的是,这段代码中还调用了一个函数cvt,我们把相关代码也复制过来:use std::io::{self, ErrorKind};#[doc(hidden)]pub trait IsMinusOne { fn is_minus_one(&self) -> bool;}macro_rules! impl_is_minus_one { ($($t:ident)) => ($(impl IsMinusOne for $t { fn is_minus_one(&self) -> bool { self == -1 } }))}impl_is_minus_one! { i8 i16 i32 i64 isize }pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> { if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) }}pub fn cvt_r<T, F>(mut f: F) -> io::Result<T> where T: IsMinusOne, F: FnMut() -> T{ loop { match cvt(f()) { Err(ref e) if e.kind() == ErrorKind::Interrupted => {} other => return other, } }}还记得上一节我们使用过的last_os_error()方法么,这段代码通过宏impl_is_minus_one为 i32等常见类型实现了IsMinusOne这个Tarit,然后我们就可以使用cvt函数更便捷得调用last_os_error()取得错误。 我将这段代码放到util.rs文件中,并在main.rs文件上方加入pub mod util;然后再来看FileDesc最终的实现:use std::mem;use std::io;use std::cmp;use std::os::unix::io::FromRawFd;use libc as c;use crate::util::cvt;#[derive(Debug)]pub struct FileDesc(c::c_int);pub fn max_len() -> usize { <c::ssize_t>::max_value() as usize}impl FileDesc { pub fn raw(&self) -> c::c_int { self.0 } pub fn into_raw(self) -> c::c_int { let fd = self.0; mem::forget(self); fd } pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { let ret = cvt(unsafe { c::read( self.0, buf.as_mut_ptr() as *mut c::c_void, cmp::min(buf.len(), max_len()) ) })?; Ok(ret as usize) } pub fn write(&self, buf: &[u8]) -> io::Result<usize> { let ret = cvt(unsafe { c::write( self.0, buf.as_ptr() as *const c::c_void, cmp::min(buf.len(), max_len()) ) })?; Ok(ret as usize) } pub fn get_cloexec(&self) -> io::Result<bool> { unsafe { Ok((cvt(libc::fcntl(self.0, c::F_GETFD))? & libc::FD_CLOEXEC) != 0) } } pub fn set_cloexec(&self) -> io::Result<()> { unsafe { cvt(c::ioctl(self.0, c::FIOCLEX))?; Ok(()) } } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { unsafe { let v = nonblocking as c::c_int; cvt(c::ioctl(self.0, c::FIONBIO, &v))?; Ok(()) } } pub fn duplicate(&self) -> io::Result<FileDesc> { cvt(unsafe { c::fcntl(self.0, c::F_DUPFD_CLOEXEC, 0) }).and_then(|fd| { let fd = FileDesc(fd); Ok(fd) }) }}impl FromRawFd for FileDesc { unsafe fn from_raw_fd(fd: c::c_int) -> FileDesc { FileDesc(fd) }}impl Drop for FileDesc { fn drop(&mut self) { let _ = unsafe { c::close(self.0) }; }}我已经将与Linux不相关的代码删除掉了。之所以原有duplicate那么冗长,是因为旧的Linux内核不支持F_DUPFD_CLOEXEC这个设置。fcntl这个函数,用来设置控制文件描述符的选项,我们稍后还会遇到用来设置和获取套接字的getsockopt和setsockopt。还有read_at和write_at等实现比较复杂的函数,我们用不到,也将他们删除。还有impl<‘a> Read for &‘a FileDesc ,因为内部使了一个Unstable的API,我也将其去掉了。我自由发挥了一下,把:pub struct FileDesc { fd: c_int,}替换成了:pub struct FileDesc(c::c_int);它们是等效的。不知你注意到没有,我把pub fn new(…)函数给去掉了,因为这个函数是unsafe的—-如果我们今后将这些代码作为库让别人使用的话,他可能传入了一个不存在的描述符,并由此可能引起程序崩溃—-但他们并不一定知道。我们可以通过在这个函数前面加unsafe来告诉使用者这个函数是unsafe的: pub unsafe fn new(…)。不过,Rust的开发者们已经考虑到了这一点,我们用约定俗成的from_raw_fd来代替pub unsafe fn new(…),于是才有了下面这一段:impl FromRawFd for FileDesc { unsafe fn from_raw_fd(fd: c::c_int) -> FileDesc { FileDesc(fd) }}最后,还利用Rust的drop实现了close函数,也就意味着,描述符离开作用域后,会自动close,就不再需要我们手动close了。与之先关的是into_raw方法,意思是把FileDesc转换为“未加工的”或者说是“裸的”描述符,也就是C的描述符。这个方法里面调用了forget,之后变量离开作用域后,就不会调用drop了。当你使用这个方法拿到描述符,使用完请不要忘记手动close或者再次from_raw_fd。pub fn into_raw(self) -> c::c_int { let fd = self.0; mem::forget(self); fd}我将这段代码放到了一个新的文件fd.rs中,并在main.rs文件上方加入pub mod fd;。接着,我们还需一个Socket类型,将socket、bind、connect等函数附加上去。这一步应该简单多了。同时你也会发现,我们已经把unsafe的代码,封装成了safe的代码。use std::io;use std::mem;use std::os::unix::io::{RawFd, AsRawFd, FromRawFd};use libc as c;use crate::fd::FileDesc;use crate::util::cvt;pub struct Socket(FileDesc);impl Socket { pub fn new(family: c::c_int, ty: c::c_int, protocol: c::c_int) -> io::Result<Socket> { unsafe { cvt(c::socket(family, ty | c::SOCK_CLOEXEC, protocol)) .map(|fd| Socket(FileDesc::from_raw_fd(fd))) } } pub fn bind(&self, storage: *const c::sockaddr, len: c::socklen_t) -> io::Result<()> { self.setsockopt(c::SOL_SOCKET, c::SO_REUSEADDR, 1)?; cvt(unsafe { c::bind(self.0.raw(), storage, len) })?; Ok(()) } pub fn listen(&self, backlog: c::c_int) -> io::Result<()> { cvt(unsafe { c::listen(self.0.raw(), backlog) })?; Ok(()) } pub fn accept(&self, storage: *mut c::sockaddr, len: *mut c::socklen_t) -> io::Result<Socket> { let fd = cvt(unsafe { c::accept4(self.0.raw(), storage, len, c::SOCK_CLOEXEC) })?; Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) } pub fn connect(&self, storage: *const c::sockaddr, len: c::socklen_t) -> io::Result<()> { cvt(unsafe { c::connect(self.0.raw(), storage, len) })?; Ok(()) } pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) } pub fn write(&self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) } pub fn get_cloexec(&self) -> io::Result<bool> { self.0.get_cloexec() } pub fn set_cloexec(&self) -> io::Result<()> { self.0.set_cloexec() } pub fn setsockopt<T>(&self, opt: libc::c_int, val: libc::c_int, payload: T) -> io::Result<()> { unsafe { let payload = &payload as *const T as *const libc::c_void; cvt(libc::setsockopt( self.0.raw(), opt, val, payload, mem::size_of::<T>() as libc::socklen_t ))?; Ok(()) } } pub fn getsockopt<T: Copy>(&self, opt: libc::c_int, val: libc::c_int) -> io::Result<T> { unsafe { let mut slot: T = mem::zeroed(); let mut len = mem::size_of::<T>() as libc::socklen_t; cvt(libc::getsockopt( self.0.raw(), opt, val, &mut slot as *mut T as *mut libc::c_void, &mut len ))?; assert_eq!(len as usize, mem::size_of::<T>()); Ok(slot) } }}impl FromRawFd for Socket { unsafe fn from_raw_fd(fd: RawFd) -> Socket { Socket(FileDesc::from_raw_fd(fd)) }}impl AsRawFd for Socket { fn as_raw_fd(&self) -> RawFd { self.0.raw() }}我已经将上一节中我们使用到的socket相关的主要的5个函数,外加read,write,等几个描述符设置的函数,“依附”到了Socket上。保存在 socket.rs 文件里。要说明的是,我在new和accept方法中,通过flags直接为新创建的描述符设置了SOCK_CLOEXEC选项,如果不想一步设置的话,就需要创建出描述符后,再调用set_cloexec方法。bind中,在调用c::bind之前,我给套接字设置了个选项SO_REUSEADDR,意为允许重用本地地址,这里不展开讲,如果你细心的话就会发现,上一节的例子,如果没有正常关闭socket的话,就可能会出现error:98,Address already in use,等一会儿才会好。accept4不是个标准的方法,只有Linux才支持,我们暂时不考虑兼容性。setsockopt和getsockopt方法中涉及到了类型转换,结合前面的例子,这里应该难不倒你了。除了from_raw_fd,我还又给Socket实现了又一个约定俗成的方法as_raw_fd。我已经将远吗放到了这里,你可以去查看。你还可以尝试将上一节的例子,修改成我们今天封装的Socket。这一节到这里就结束了。 ...

April 15, 2019 · 6 min · jiezi

在 RustCon Asia 开启之前,聊聊 Rust 中国社区那些事

亚洲首届 RustCon Asia 将在 4 月 20 日于北京开启(也就是下周六啦~),大会为期 4 天,包括 20 日全天和 21 日上午的主题演讲以及 22-23 日的多个主题 workshop 环节。随着大会渐渐临近,很多小伙伴已经兴奋的搓搓手了,不如今天来聊聊 Rust 中国社区的成长史,再打一波鸡血?Rust 在中国2012 年 1 月 24 日,在中国最大的问答社区「知乎」,名为“题叶”的网友,创建了 「Rust(编程语言)」话题,那时候这门语言还无人问津。2013 年 9 月 16 日,这个知乎栏目的 Logo 才被换成 Rust 的符号, 2016 年增加了对 Rust 的中文介绍,期间陆陆续续添加了一些子话题,发展至今已经有 8000+ 的关注量。而在最新出炉的 Stack Overflow 开发者调查中,Rust 连续 4 年成为最受开发者喜爱的编程语言(https://insights.stackoverflow.com/survey/2019)。这里不得不提到项目方对社区的支持,中国的 Rust 开发者所熟知的两家公司 PingCAP 和秘猿科技一直在致力于 Rust 的推广。2017 年 4 月,PingCAP 在北京举办了 Rust Meetup,邀请到两位 Rust 团队核心成员 Alex Crichton、Brian Anderson,和 PingCAP 首席架构师唐刘一起,与 100 余位 Rust 中国社区的小伙伴进行了深入交流。同年 10 月,PingCAP 邀请 web 框架 Rocket 的作者 Sergio Benitez,首次为中国小伙伴深入介绍这个在 Rust 社区颇受欢迎的 web 框架。通过这两次 Meetup,越来越多的中国社区小伙伴被 Rust 语言所吸引,并开始用 Rust 折腾自己的“小天地”。2018 年的 11 月 7 日,秘猿科技在杭州举办了第一场以 Rust 语言为主题的线下活动,Bilibili 在线直播达到了 2000+ 人同时观看。在杭州的冬季,这一次直播,再次点燃了 Rust 中国社区。2018 年,F001 的新书《深入浅出 Rust》发布,这是第一本正式出版发行的中文原创 Rust 书籍,覆盖了 Rust 大部分的初级和中级知识点。2019 年 1 月 1 日,张汉东老师完成了《Rust 编程之道》的出版,目前在京东上累计评价 700+。另外张汉东老师早期参与运营了 Rust 中文社区,并在Rust.CC 论坛、GitHub 、语雀订阅开通了 Rust 日报。社区小伙伴的加入之后,Rust 日报小组正式成立,不断为大家收集更多海内外最新的开发和社区上的各种信息。除了官方的社区阵地,Rust 社区自发的 This Week in Rust、Rust 日报以及 Slack、Discord 平台上的各地 Rust 小组、微信& QQ 交流群等各种组织也在增长和活跃;国内外知名企业、初创公司在 Rust 应用上的实践文章和书籍出版数量也在不断增长……越来越多的人在自发推进 Rust 语言的快速成长和应用实践,作为一个用来设计取代 C++ 的编程语言,Rust 在未来还将有非常广阔的拓展空间。RustCon Asia 的到来4 月 20 日,在中国北京,秘猿科技和 PingCAP 将携手开启中国首届 Rust 社区大会 —— RustCon Asia。在去年参加完 RustFest 的时候我们遇到了很多 Rust 社区的朋友,获得了来自这些社区的朋友们和 Mozilla 的支持。中国社区的小伙伴激动地说,他最喜欢的两家公司联手了!随着 Rust 的社区的扩大,Rust 语言本身的优势让其在生产环境的应用快速丰富起来,我们看到大大小小的公司都在尝试和实践。在 RustCon Asia,你将看到很多优秀 Rust 项目:蚂蚁金服时序数据库阿里云城市大脑淘宝广告推荐算法字节跳动用 Rust 实现 im sdk百度 X-Lab:Rust-SGXBilibili 中间件知乎搜索引擎秘猿科技的许可链 CITAPingCAP 用 Rust 开发 TiKV公链项目 Nervos、Holochain百度安全实验室的 MesaLink ……这一次,小伙伴们将有机会深度接触来自海内外的二十七位讲师。有社区小伙伴回复说,他觉得 讲师介绍系列 很不错,认真看了每个人的背景故事之后,发现这些讲师比他想象中的更厉害,还有同学说,今天的 Rust 社区很有当年 Ruby 社区的感觉,充满了奇人异事。RustCon Asia 的开启让我们看到 Rust 社区其实比我们想象的更加壮大。这一次,来自国内外的相聚(大型粉丝见面会),一天半的主题演讲和两天三场同时进行的 workshop,相信大家一定会收获到自己想要的知识、近距离接触这些技术大牛,这将是 Rust 中国社区发展史上的重要时刻。一起拥抱 Rust!大会已进入一周倒计时,我们在这里提前感谢前来现场的讲师们、参会的社区小伙伴们,以及此次大会的金牌赞助商百度 X-Lab 和铜牌赞助商量子链、SNZ,以及各位帮助推广的小伙伴们,感谢大家对此次大会的支持!百度 X-Lab官网:https://anquan.baidu.com/百度安全以技术开源、专利共享、标准驱动为理念,联合互联网公司、安全厂商、终端制造商、 高校及科研机构, 推动 AI 时代的安全生态建设,让全行业享受更安全的 AI 所来带来的变革。量子链官网:https://qtum.org/zhQtum 量子链是一个开源区块链项目,是建立在 UTXO 模型之上、采用 PoS 共识机制和去中心化治理机制、且兼容多虚拟机的价值传输网络和智能合约平台。通过打造商业化智能合约、创造可信去中心化应用和提供企业级区块链服务全方位赋能商业生态。SNZ官网:Snzholding.comSNZ 是一家快速发展的加密资产基金、咨询机构和社区建设者。SNZ 团队由一群工程师,技术推广人员和企业家组成,他们对区块链技术抱有一致的信念。SNZ 的使命是发现有价值的项目,为团队带来资源,为生态系统做出贡献。团队正尽最大努力帮助伟大的项目在中国发展业务,并将当地项目和社区与海内外同行连接起来。活动时间:4 月 20-23 日活动地点:北京 · 朝阳广顺南大街 8 号北京望京凯悦酒店大会官网:https://rustcon.asia/Twitter @RustConAsia购票地址:https://www.huodongxing.com/event/6479456003900 ...

April 15, 2019 · 2 min · jiezi

Rust 实战 - 使用套接字联网API (一)

虽然标准库已经封装好了 TcpListener 和TcpStream 等基础api,但作为Rust 的爱好者,我们可以去一探究竟。本文假设你已经对 Rust 和 Linux 操作系统有了一定了解。在 Linux 上 Rust 默认会链接的系统的 libc 以及一些其他的库,这就意味着,你可以直接使用libc中的函数。比如,你可以使用 gethostname 获取你电脑的 “hostname”:use std::os::raw::c_char;use std::ffi::CStr;extern { pub fn gethostname(name: *mut c_char, len: usize) -> i32;}fn main() { let len = 255; let mut buf = Vec::<u8>::with_capacity(len); let ptr = buf.as_mut_ptr() as *mut c_char; unsafe { gethostname(ptr, len); println!("{:?}", CStr::from_ptr(ptr)); }}解释一下上面的代码。extren 表示“外部块(External blocks)”,用来申明外部非 Rust 库中的符号。我们需要使用 Rust 以外的函数,比如 libc ,就需要在 extren 中将需要用到的函数定义出来,然后就可以像使用本地函数一样使用外部函数,编译器会负责帮我们转换,是不是很方便呢。但是,调用一个外部函数是unsafe的,编译器不能提供足够的保证,所以要放到unsafe块中。如果外部函数有可变参数,可以这么申明:extern { fn foo(x: i32, …);}不过 Rust 中的函数目前还不支持可变参数。实际上,这里应该是 extern “C” { .. },因为默认值就是"C",我们就可以将其省略。还有一些其他的可选值,因为这里不会用到,暂且不讨论,你可以去这儿这儿查看。再来说说类型。“gethostname” 函数在 C 头文件中的原型是:int gethostname(char name, size_t len);在 Linux 64位平台上,C中的int对应于Rust中的int,size_t对应Rust中的usize,但C中的char与Rust中的char是完全不同的,C中的char始终是i8或者u8,而 Rust 中的char是一个unicode标量值。你也可以去标准库查看。对于指针,Rust 中的裸指针 与C中的指针几乎是一样的,Rust的mut对应C的普通指针,*const 对应C的const指针。因此我们将类型一一对应,函数的参数名称不要求一致。pub fn gethostname(name: *mut i8, len: usize) -> i32;但是,我们后面会使用CStr::from_ptr()将C中的字符串转换为 Rust 本地字符串,这个函数的定义是:pub unsafe fn from_pt<‘a>(ptr: *const c_char) -> &‘a CStr为了“好看”一点,我就写成了c_char,但是,c_char只是i8的别名,你写成i8也没有问题的。type c_char = i8;你可以看这里。不过,如果你要是考虑跨平台的话,可能需要吧 i32 换成 std::os::raw::c_int,并不是所有平台上C中的int都对应Rust中的i32。不过,如果你没有一一对应类型,一定程度上是可行的,如果没有发生越界的话。比如像这样:use std::os::raw::c_char;use std::ffi::CStr;extern { pub fn gethostname(name: *mut c_char, len: u16) -> u16;}fn main() { let len = 255; let mut buf = Vec::<u8>::with_capacity(len); let ptr = buf.as_mut_ptr() as *mut c_char; unsafe { gethostname(ptr, len as u16); println!("{:?}", CStr::from_ptr(ptr)); }}我把 size_t 和 int 都对应成了 u16,这段代码是可以编译通过,并正确输出你的hostname的,但我建议,你最好是将类型一一对应上,以减少一些不必要的麻烦。当然,你把那个 *mut c_char 换成 *mut i32,也没问题,反正都是个指针,你可以试试:use std::os::raw::c_char;use std::ffi::CStr;extern { pub fn gethostname(name: *mut i32, len: u16) -> u16;}fn main() { let len = 255; let mut buf = Vec::<u8>::with_capacity(len); let ptr = buf.as_mut_ptr() as *mut i32; unsafe { gethostname(ptr, len as u16); println!("{:?}", CStr::from_ptr(ptr as *const i8)); }}你还可以把 Vec::<u8>换成Vec::<i32> 看看结果。int gethostname(char *name, size_t len) 这个函数,是接收一个char数组和数组长度,也可以说成接收缓冲区和接收缓冲区的最大长度。我是创建了一个容量为255的Vec<u8>,将其可变指针转换为裸指针。你也可以创建可以长度为255的u8数组,也没有问题: let len = 255; let mut buf = [0u8; 255]; let ptr = buf.as_mut_ptr() as *mut i32; unsafe { gethostname(ptr, len as u16); println!("{:?}", CStr::from_ptr(ptr as *const i8)); }为什么这样可以,因为Rust的Slice和Vec的底层内存布局,跟C是一样的。(注意,Rust中Slice与Array的关系,就像&str与str的关系)。我们可以看看Vec和Slice在源码中的定义:pub struct Vec<T> { buf: RawVec<T>, len: usize,}pub struct RawVec<T, A: Alloc = Global> { ptr: Unique<T>, cap: usize, a: A,}pub struct Unique<T: ?Sized> { pointer: *const T, _marker: PhantomData<T>,}struct FatPtr<T> { data: const T, len: usize,}Vec是一个结构体,里面包含buf和len两个字段,len用来表示Vec的长度,buf又指向另一个结构体RawVec,其中有三个字段,第三个字段a是一个Tarit,不占内存。cap用来表示Vec的容量,ptr指向另一个结构体Unique,其中的pointer字段就是一个裸指针了,_marker是给编译器看的一个标记,也不占内存,暂时不讨论这个,你可以去看文档。Slice的结构更简单,就一个裸指针和长度。虽然RawVec和Unique在标准库外部是不可见的,但我们还是能用一定的“手段”取出里面值,那就是定义一个内存布局跟Vec一样的结构体,“强行”转换。#[derive(Debug)]struct MyVec<T> { ptr: mut T, cap: usize, len: usize}我定义了一个叫做MyVec的结构体,忽略了Vec中两个不占用内存的字段,他们的内存布局是相同的,在64位平台上都是24(ptr占8个,另外两个usize个8个)个字节。你可以试试:#[derive(Debug)]struct MyVec<T> { ptr: mut T, cap: usize, len: usize}println!("{:?}", std::mem::size_of::<Vec<u8>>());println!("{:?}", std::mem::size_of::<MyVec<u8>>());我先创建一个Vec<u8>,拿到Vec<u8>的裸指针const Vec<u8>,再将const Vec<u8>转换为const MyVec<u8>,之后,解引用,就能得到MyVec<u8>了。不过,解引裸指针是unsafe的,要谨慎!!! 你还可以看看标准库中讲述pointer的文档。fn main() { let vec = Vec::<u8>::with_capacity(255); println!(“vec ptr: {:?}”, vec.as_ptr()); #[derive(Debug)] struct MyVec<T> { ptr: *mut T, cap: usize, len: usize } let ptr: *const Vec<u8> = &vec; let my_vec_ptr: *const MyVec<u8> = ptr as _; unsafe { println!("{:?}", *my_vec_ptr); }}然后编译运行,是否可以看到类似下面的输出呢:vec ptr: 0x557933de6b40MyVec { ptr: 0x557933de6b40, cap: 255, len: 0 }你可以看到,我们调用vec.as_ptr()得到的就是Vec内部的那个裸指针。对于std::mem::size_of 相等的两个类型,你也可以使用std::mem::transmute 这个函数转换,跟上面的通过裸指针间接转换,几乎是等效的,只是会多加一个验证,如果两个类型size_of不相等的话,是无法通过编译的。这个函数是unsafe的。你还可以继续尝试,比如把Vec<u8>转换为长度为3(或者更小更大)的usize数组,像是这样:fn main() { let vec = Vec::<u8>::with_capacity(255); println!(“vec ptr: {:?}”, vec.as_ptr()); let ptr: *const Vec<u8> = &vec; unsafe { let aaa_ptr: *const [usize; 2] = ptr as _; println!("{:?}", (*aaa_ptr)[0] as *const u8); }}不过,由于Rust中Vec的扩容机制,这段代码是存在一定问题的:fn main() { let len = 255; let mut buf = Vec::<u8>::with_capacity(len); let ptr = buf.as_mut_ptr() as *mut c_char; unsafe { gethostname(ptr, len); println!("{:?}", CStr::from_ptr(ptr)); } println!("{:?}", buf);}虽然获取到了正确的主机名,但是之后你打印buf会发现,buf是空的,这个问题留给你去探究。你已经看到,Rust已经变得“不安全”,这又不小心又引入了另一个话题–《 Meet Safe and Unsafe》。不过,还是尽快回归正题,等之后有机会再说这个话题。说起套接字API,主要包括TCP、UDP、SCTP相关的函数,I/O复用函数和高级I/O函数。其中大部分函数Rust标准里是没有的,如果标准库不能满足你的需求,你可以直接调用libc中的函数。实际上,标准库中,网络这一块,也基本是对libc中相关函数的封装。先从TCP开始。TCP套接字编程主要会涉及到socket、connect、bind、listen、accept、close、getsockname、getpeername等函数。先来看看这些函数的定义:// socket 函数用来指定期望的通信协议类型,并返回套接字描述符int socket(int family, int type, int protocol); // 成功返回监听描述符。用来设置监听,出错为-1// family是表示socket使用的协议类型,对于TCP,通常设置为 AF_INET 或AF_INET6,表示IPv4和IPv6// type是创建的套接字类型,TCP是字节流套接字,所以这里设置为SOCK_STREAM,可选的值还有// SOCK_DGRAM用于UDP,SOCK_SEQPACKET用于SCTP// protocol协议的标识,可以设置为0,让系统选择默认值。可选的值有IPPROTO_TCP、IPPROTO_UDP、IPPROTO_SCTP// connect 函数被客户端用来联立与TCP服务器的连接int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen); // 成功返回0 ,出错为-1// sockfd 是由 socket 函数返回的套接字描述符,第二和第三个参数分别指向一个指向套接字地址结构的指针和该指针的长度// bind 函数把一个本地协议地址赋予一个套接字。int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen); // 成功返回0 ,出错为-1// 第二个和第三个参数分别是指向特点于协议的地址结构的指针和指针的长度// listen 函数把一个未连接的套接字转换成一个被动套接字,指示内核应接受指向该套接字的连接请求。int listen(int sockfd, int backlog); // 成功返回0 ,出错为-1// 第二个参数指定内核该为相应套接字排队的最大连接个数。// accept 函数由TCP服务器调用,用于从已完成连接的队列头返回下一个已完成的连接。int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen); // 成功返回非负描述符,错误返回-1// 第二个和第三个参数用来返回客户端的协议地址和该地址的大小// close 用来关闭套接字,并终止TCP连接int close(int sockfd); // 成功返回0 ,出错为-1// getsockname 和 getpeername 函数返回与某个套接字关联的本地协议地址和外地协议地址int getsockname(int sockfd,struct sockaddr *localaddr,socklen_t *addrlen); // 成功返回0 ,出错为-1int getpeername(int sockfd,struct sockaddr *peeraddr,socklen_t *addelen); // 成功返回0 ,出错为-1还有一对常见的函数,read 和 write 用于读写数据。另外还有三对高级I/O函数,recv/send、readv/writev和recvmsg/sendmsg等需要的时候再加。ssize_t read(int fd, void *buf, size_t count);ssize_t write(int fd, const void *buf, size_t count);除了函数外,还有几个常量和sockaddr这个结构体。常量我们需要在Rust这边定义出来,只定义出需要的:const AF_INET: i32 = 2;const AF_INET6: i32 = 10;const SOCK_STREAM: i32 = 1;const IPPROTO_TCP: i32 = 6;除了sockaddr外,还有几个与之相关的结构体,他们在C中的定是:struct sockaddr{ unsigned short int sa_family; // 地址族 unsigned char sa_data[14]; // 包含套接字中的目标地址和端口信息};struct sockaddr_in{ sa_family_t sin_family; uint16_t sin_port; struct in_addr sin addr; char sin_zero[8];};struct in_addr{ In_addr_t s_addr;};struct sockaddr_in6{ sa_family_t sin_family; in_port_t sin6_port; uint32_t sin6_flowinfo; struct in6_addr sin6_addr; uint32_t sin6_scope_id;};struct in6_addr{ uint8_t s6_addr[16]};struct sockaddr_storage { sa_family_t ss_family; // address family // all this is padding, implementation specific, ignore it: char __ss_pad1[_SS_PAD1SIZE]; int64_t __ss_align; char __ss_pad2[_SS_PAD2SIZE];};然后,需要在Rust中定义出布局相同的结构体:#[repr(C)]#[derive(Debug, Clone, Copy)]pub struct sockaddr { pub sa_family: u16, pub sa_data: [c_char; 14],}#[repr(C)]#[derive(Debug, Clone, Copy)]pub struct sockaddr_in { pub sin_family: u16, pub sin_port: u16, pub sin_addr: in_addr, pub sin_zero: [u8; 8],}#[repr(C)]#[derive(Debug, Clone, Copy)]pub struct in_addr { pub s_addr: u32,}#[repr(C)]#[derive(Debug, Clone, Copy)]pub struct sockaddr_in6 { pub sin6_family: u16, pub sin6_port: u16, pub sin6_flowinfo: u32, pub sin6_addr: in6_addr, pub sin6_scope_id: u32,}#[repr(C)]#[derive(Debug, Clone, Copy)]pub struct in6_addr { pub s6_addr: [u8; 16],}#[repr(C)]#[derive(Debug, Clone, Copy)]pub struct sockaddr_storage { pub ss_family: u16, _unused: [u8; 126]}你需要在结构体前面加一个#[repr(C)]标签,以确保结构体的内存布局跟C一致,因为,Rust结构体的内存对齐规则,可能跟C是不一样的。#[derive(Debug, Clone, Copy)] 不是必须的。对于最后一个结构体sockaddr_storage,我也很迷,我不知道在Rust中如何定义出来,但是我知道它占128个字节,然后我就定义一个长度为126的u8数组,凑够128位。接下来,继续把那几个函数定义出来:extern { pub fn socket(fanily: i32, ty: i32, protocol: i32) -> i32; pub fn connect(sockfd: i32, servaddr: *const sockaddr, addrlen: u32) -> i32; pub fn bind(sockfd: i32, myaddr: *const sockaddr, addrlen: u32) -> i32; pub fn listen(sockfd: i32, backlog: i32); pub fn accept(sockfd: i32, cliaddr: *mut sockaddr, addrlen: u32) -> i32; pub fn close(sockfd: i32) -> i32; pub fn getsockname(sockfd: i32, localaddr: *mut sockaddr, addrlen: *mut u32) -> i32; pub fn getpeername(sockfd: i32, peeraddr: *mut sockaddr, addrlen: *mut u32) -> i32; pub fn read(fd: i32, buf: *mut std::ffi::c_void, count: usize) -> isize; pub fn write(fd: i32, buf: const std::ffi::c_void, count: usize) -> isize;}对于read 和 write 里的参数buf类型void, 可以使用标准库提供的 std::ffi::c_void,也可以是mut u8/*const u8,像是下面这样:pub fn read(fd: i32, buf: *mut u8, count: usize) -> isize;pub fn write(fd: i32, buf: *const u8, count: usize) -> isize;或者,既然void本身是个“动态类型”,也可以传个其他类型的指针进去的,之后你可以试试,不过可能会有点危险。看看目前的代码:use std::os::raw::c_char;use std::ffi::c_void;pub const AF_INET: i32 = 2;pub const AF_INET6: i32 = 10;pub const SOCK_STREAM: i32 = 1;pub const IPPRPTO_TCP: i32 = 6;#[repr(C)]#[derive(Debug, Clone, Copy)]pub struct sockaddr { pub sa_family: u16, pub sa_data: [c_char; 14],}#[repr(C)]#[derive(Debug, Clone, Copy)]pub struct sockaddr_in { pub sin_family: u16, pub sin_port: u16, pub sin_addr: in_addr, pub sin_zero: [u8; 8],}#[repr(C)]#[derive(Debug, Clone, Copy)]pub struct in_addr { pub s_addr: u32,}#[repr(C)]#[derive(Debug, Clone, Copy)]pub struct sockaddr_in6 { pub sin6_family: u16, pub sin6_port: u16, pub sin6_flowinfo: u32, pub sin6_addr: in6_addr, pub sin6_scope_id: u32,}#[repr(C)]#[derive(Debug, Clone, Copy)]pub struct in6_addr { pub s6_addr: [u8; 16],}#[repr(C)]#[derive(Clone, Copy)]pub struct sockaddr_storage { pub ss_family: u16, _unused: [u8; 126]}extern { pub fn socket(fanily: i32, ty: i32, protocol: i32) -> i32; pub fn connect(sockfd: i32, servaddr: *const sockaddr, addrlen: u32) -> i32; pub fn bind(sockfd: i32, myaddr: *const sockaddr, addrlen: u32) -> i32; pub fn listen(sockfd: i32, backlog: i32); pub fn accept(sockfd: i32, cliaddr: *mut sockaddr, addrlen: *mut u32) -> i32; pub fn close(sockfd: i32) -> i32; pub fn getsockname(sockfd: i32, localaddr: *mut sockaddr, addrlen: *mut u32) -> i32; pub fn getpeername(sockfd: i32, peeraddr: *mut sockaddr, addrlen: *mut u32) -> i32; pub fn read(fd: i32, buf: *mut std::ffi::c_void, count: usize) -> isize; pub fn write(fd: i32, buf: *const std::ffi::c_void, count: usize) -> isize;}然后,我们可以写一个简单的服务器和客户端程序:服务器监听一个地址,客户端连接服务器,然后向服务器发送“Hello, server!”,服务器回应“Hi,client!”,客户端收到后断开连接。fn main() { use std::io::Error; use std::mem; use std::thread; use std::time::Duration; thread::spawn(|| { // server unsafe { let socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if socket < 0 { panic!(“last OS error: {:?}”, Error::last_os_error()); } let servaddr = sockaddr_in { sin_family: AF_INET as u16, sin_port: 8080u16.to_be(), sin_addr: in_addr { s_addr: u32::from_be_bytes([127, 0, 0, 1]).to_be() }, sin_zero: mem::zeroed() }; let result = bind(socket, &servaddr as *const sockaddr_in as *const sockaddr, mem::size_of_val(&servaddr) as u32); if result < 0 { println!(“last OS error: {:?}”, Error::last_os_error()); close(socket); } listen(socket, 128); loop { let mut cliaddr: sockaddr_storage = mem::zeroed(); let mut len = mem::size_of_val(&cliaddr) as u32; let client_socket = accept(socket, &mut cliaddr as *mut sockaddr_storage as *mut sockaddr, &mut len); if client_socket < 0 { println!(“last OS error: {:?}”, Error::last_os_error()); break; } thread::spawn(move || { loop { let mut buf = [0u8; 64]; let n = read(client_socket, &mut buf as *mut _ as *mut c_void, buf.len()); if n <= 0 { break; } println!("{:?}", String::from_utf8_lossy(&buf[0..n as usize])); let msg = b"Hi, client!"; let n = write(client_socket, msg as *const _ as *const c_void, msg.len()); if n <= 0 { break; } } close(client_socket); }); } close(socket); } }); thread::sleep(Duration::from_secs(1)); // client unsafe { let socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if socket < 0 { panic!(“last OS error: {:?}”, Error::last_os_error()); } let servaddr = sockaddr_in { sin_family: AF_INET as u16, sin_port: 8080u16.to_be(), sin_addr: in_addr { s_addr: u32::from_be_bytes([127, 0, 0, 1]).to_be() }, sin_zero: mem::zeroed() }; let result = connect(socket, &servaddr as *const sockaddr_in as *const sockaddr, mem::size_of_val(&servaddr) as u32); if result < 0 { println!(“last OS error: {:?}”, Error::last_os_error()); close(socket); } let msg = b"Hello, server!"; let n = write(socket, msg as *const _ as *const c_void, msg.len()); if n <= 0 { println!(“last OS error: {:?}”, Error::last_os_error()); close(socket); } let mut buf = [0u8; 64]; let n = read(socket, &mut buf as mut _ as mut c_void, buf.len()); if n <= 0 { println!(“last OS error: {:?}”, Error::last_os_error()); } println!("{:?}", String::from_utf8_lossy(&buf[0..n as usize])); close(socket); }}调用外部函数是unsafe的,我为了简单省事,暂时把代码放到了一个大的unsafe {} 中,之后我们再把他们封装成safe的API。为了方便测试,我把服务器程序放到了一个线程里,然后等待1秒后,再让客户端建立连接。std::io::Error::last_os_error 这个函数,是用来捕获函数操作失败后,内核反馈给我们的错误。在调用bind 和 connect 函数时,先要创建sockaddr_in结构体,端口(sin_port)和IP地址(s_addr) 是网络字节序(big endian),于是我调用了u16和u32的to_be()方法将其转换为网络字节序。u32::from_be_bytes 函数是将[127u8, 0u8, 0u8, 1u8] 转换为u32整数,由于我们看到的已经是大端了,转换回去会变成小端,于是后面又调用了to_be(),你也可以直接u32::from_le_bytes([127, 0, 0, 1])。然后使用了std::mem::zeroed 函数创建一个[0u8; 8] 数组,你也可以直接[0u8; 8],在这里他们是等效的。接着,我们进行强制类型转换,将&sockaddr_in 转换为const sockaddr_in类型,又继续转换为const sockaddr,如果你理解了一开始“gethostname”那个例子话,这里应该很好理解。这里还可以简写成&servaddr as *const _ as *const _,编译器会自动推导类型。在调用accept函数时,先创建了一个mut sockaddr_storage,同样进行类型转换。之所以用sockaddr_storage 而不是sockaddr_in和sockaddr_in6是因为sockaddr_storage这个通用结构足够大,能承载sockaddr_in和sockaddr_in6等任何套接字的地址结构,因此,我们如果把套接bind到一个IPv6地址上的话,这里的代码是不需要修改的。我还是用std::mem::zeroed 函数初始化sockaddr_storage,它的结构我也很迷惑,所以就借助了这个函数,这个函数是unsafe的,使用的时候要小心。你也可以继续尝试这个函数:let mut a: Vec<u8> = unsafe { std::mem::zeroed() };a.push(123);println!("{:?}", a);在read 和 write 时,同样要类型转换。很多时候,类型根本“强不起来”。OK,这一节的内容就先到这里。 ...

April 14, 2019 · 7 min · jiezi

rust的私有代码仓库

背景公司内部要使用或推广必须要满足私有性和保密性,公司内部的代码是不可以公开到公网上的。rust的公开代码仓库是crates.io,那么私有仓库的解决方案是什么?我们一起来探索一下。国外网站reddit有人发出了对私有仓库的需求并提问到:Private “crates.io"Hi,I’m trying to advocate for Rust in my company, and one of the needs will be to have our own “crates.io” so our (closed source) crates can be managed by cargo.Is there a way to do that appart from specifying git urls?另外,这位程序员还不想通过git来管理仓库,因为他的公司不用git。withoutboats大牛的解决方案来了!2017年10月,他说道:本周,cargo获得了一个新功能!现在您可以从其他仓库下载依赖包也可以从ractes.io下载的依赖包。这是重要的一步,使组织能够通过cargo分发其内部代码库,而无需将这些内部代码库上传到公共仓库。此功能将仅在夜间可用,并用alternative-registries这个feature gate来进行封闭。自1.0版本以来,我们已经使用feature gate 迭代rustc中新的不稳定的特性,但这是我们第一次在Cargo中使用feature gate。这里我解释一下此段话。因为rust的发布版本是很有节奏的,按每六周一个版本的节奏分为nightly、beta、stable三种版本。rust还在发展阶段,版本迭代节奏快,开发团队想要快速发新功能并在外部快速得到验证,每个在开发的功能都会发布在nightly版本。nightly版本是不稳定的,发布后再过六周就会自动变为beta版本(期间会修改bug)。beta版本再过六周就会成功稳定版本。节奏图如下:nightly: * - - * - - * - - * - - * - - * - * - * | |beta: * - - - - - - - - * * |stable: *我们继续大牛的讲述:自1.0之前起,cargo已与crate.io仓库结合成一对。这个仓库包含数千个crate,它们构成了rust的代库库生态系统的大部分,cargo使下载和使用crates.io中的代码库变得非常方便。但是,有很多原因使用户可能不希望将其代码库上传到cractes.io。特别是,专有代码不适用于crates.io,crates.io允许任何人下载它包含的任何crate。因此,cargo总是支持其他来源的仓库。具体来说,它支持从文件路径或Git仓库中拉取依赖包。好了,也就是说可以使用git仓库来做私有仓库。哈哈另一方面,到目前为止,cargo只允许你一次依靠一个仓库的crate。在许多情况下,像crates.io这样的仓库比git更好,因为它们能够更有效地管理多个版本的crate,并为您执行版本解析。使用这个新功能,您将能够从ractes.io以外的仓库中拉取crate。好了,而且这个功能出来后,对于同一个rust项目用户可以同时使用两种上以的仓库。实操… ...

April 13, 2019 · 1 min · jiezi

与顶尖 Rust 开发者面基指南(一) | RustCon Asia

距离 4 月 20 日 RustCon Asia 大会 开启只剩下约两周的时间了,你准备好了吗?此次 RustCon Asia 是首次在亚洲举办的 Rust 语言开发者大会,也是目前亚洲地区规格最高,参与人数规模最大的 Rust 语言大会。不仅有来自亚洲社区的大神,还有从欧洲、澳洲、北美远道而来的顶尖开发者。现场特地配备了中英双语同声传译,以便更流畅地传达演讲内容,希望大家没有顾虑的与讲师们面基!随着大会日期的不断临近,我们也逐一介绍了部分讲师及其议题,方便大家提前了解更多信息(做好功课勾搭大神:D )。今天先为大家介绍其中 8 位讲师和议题,快来看看大神们的庐山真面目吧!Nick CameronRust 语言团队核心成员Rust dev-tools 和 Cargo 团队负责人前 Mozilla Research 研究工程师此次 RustCon Asia 大会,Nick Cameron 将带来的演讲主题是《Making Rust Delightful》。Rust 的设计目标是成为符合人机工程学语言,那种易于阅读、易编写和维护的、并且是令人愉悦的编程语言!那么,语言和库的设计者是如何决定一个新的特性是否符合人机工程学?如何考虑人机工程学与其它设计需求(比如安全、性能)之间的权衡呢?Nick 将会向大家介绍 Rust 的设计理念以及一些关于语言本身、库和工具的人机工程学研究案例。另外还将和大家一起聊聊 Rust 语言团队和其他团队是如何做决策的。以及大家所关心的 Rust 的“显性与隐性”、“语法糖”和“一致性”等话题。孙茗珅美国百度 X-Lab 高级安全研究员此次 RustCon Asia 大会,孙茗珅将带来的演讲主题是《Linux From Scratch in Rust》。Rust 在储存安全和零抽象方面的出色使其成为系统编程的最佳候选者。为了提供安全的执行环境,我们使用 Rust 从头开始构建 Linux 发行版,包括构建系统,用户空间实用程序和简单的包管理系统。本次演讲主题,孙茗珅将主要关注用户空间工具箱(核心系统实用程序的集合),和大家讨论在构建工具箱时会遇到的设计挑战和问题,例如处理 I/O 标准,动态调度与静态泛通用类型、测试和覆盖问题等。Ravi ShankarMozillian开源运动支持者Servo 项目贡献者此次 RustCon Asia 大会,Ravi Shankar 将带来的演讲主题是《How Rust taught me to think about systems》。所有 Rustaceans 都知道 Rust 的 borrow checker 对新手来说是很难的。这个演讲涵盖了他作为 Rust 新手时遇到的各种各样的情况,这些情况在许多高级语言中是完全正常的,但在 Rust 中却会出现问题:为什么同样的代码在 Rust 中编译会不一样,如何理解 Rust 中的编译错误,以及最后这些又是如何改变 Ravi 的思考方式的?不撸兔子网红 B 大Erlang 粉Porus 项目作者此次 RustCon Asia 大会,不撸兔子将带来的演讲主题是《Rust for competitive programming》。competitive programming 要求开发者在极短时间内保质保量的解决问题。由于没有一个单独为 competitive programming 设计的代码库,contenders 通常必须从头开始执行数据结构和算法,十分繁琐且容易出错。 这个演讲将会告诉大家为什么对于competitive programming,Rust 是不可替代的。荆一明美国百度 X-Lab 安全科学家Rust 开源项目 MesaLink 作者此次 RustCon Asia 大会,荆一明将带来的演讲主题是《Cargo meets Autotools》。从 1.10 版本开始,只要在 Cargo.toml 中指定了cdylib crate 类型,rustc 就可以生成一个动态库,供 C 或 C FFI 使用。虽然 cargo install 命令使分发可执行文件(例如ripgrep)变得轻而易举,但它不适用于 cdylib 动态库。早在2018年,为了构建和分发用 Rust 编写的动态库,团队一直在努力实现有效的基础架构。最终使 autotools 与 Rust 工具链完美结合。现在用户可以下载源代码压缩包,解压缩并安装运行./configure && make && make install 。那么在这次分享中,他会详细聊一聊这里面的故事,也希望对社区带来帮助。Rahul Thakoor树莓派粉IRR 计划参与者在这次 RustCon Asia 大会上,Rahul 将会为大家带来《Introduction to IoT using Blynk, Rust and your Smartphone》主题分享。想要用 Rust 来利用智能手机的传感器和执行器来学习物联网的基础,并建立虚拟和物理世界的桥梁吗?在第三天的 Workshop 中,参与者不需要特别准备就可以体验嵌入式世界。Rahul 将使用 Blynk,这是一个免费的智能手机应用程序,为你的物联网项目提供拖放小部件。参与者只需要智能手机(iOS 或 Android)和运行Linux,macOS 或 Windows 的笔记本电脑就行了。Rahul 将介绍物联网的基础知识。参与者将配置虚拟 LED 和按钮,收集 GPS stream 或加速计等传感器数据,或将事件和数据发送到手机。最后,参与者将能够使用你的技能学习原型(your skills learned prototyping)制作更多有创意和有趣的项目,开辟自己的道路。参与者将更好地了解物联网项目,并从微控制器或其他硬件上开始使用嵌入式 Rust 开发。黄旭东May 项目作者在此次 RustCon Asia 大会上,黄旭东即将带来的演讲主题是《Stackful Coroutine Based Rust Async Story》。他将和大家分享基于 stackful generators 和 coroutine 的异步故事,也就是 May 的设计与实现,包括有关 generator 和 coroutine 的基本理论,coroutine 调度的整体结构,IO 子系统,同步抢占子系统以及取消机制等方方面面。同时,也会将 May 与当前 Rust 官方的异步 future 系统进行对比分析。也欢迎大家来 GitHub 给 May 提 PR,我们都爱 ka 贡献者。孙晓光知乎解压师知乎搜索工程团队负责人在本届 RustCon Asia 大会上,孙晓光将会给大家带来《Search Engine in production with Rust》主题演讲,分享知乎团队在用 Rust 开发实用搜索引擎过程中的设计选型和经验教训,也让其他 Rust 开发者能够尽可能避免知乎团队已踩过的坑,以及更顺利地将 Rust 用到开发生产中去。此次 RustCon Asia 大会为期四天,包括 20 日全天和 21 日上午的主题演讲和 22-23 日的多个主题 workshop 环节。其中主题演讲讲师来自于国内外资深 Rust 开发者和社区活跃贡献者;workshop 主题将覆盖到 Rust 开发入门和成熟技术栈或产品的实战操作和演示。活动时间:4 月 20-23 日活动地点:北京 · 朝阳广顺南大街 8 号北京望京凯悦酒店目前 RustCon Asia 还有少量余票,扫描下方二维码购买。大会官网:https://rustcon.asia/Twitter @RustConAsia ...

April 4, 2019 · 2 min · jiezi

TiKV 源码解析(五)fail-rs 介绍

作者:张博康本文为 TiKV 源码解析系列的第五篇,为大家介绍 TiKV 在测试中使用的周边库 fail-rs。fail-rs 的设计启发于 FreeBSD 的 failpoints,由 Rust 实现。通过代码或者环境变量,其允许程序在特定的地方动态地注入错误或者其他行为。在 TiKV 中通常在测试中使用 fail point 来构建异常的情况,是一个非常方便的测试工具。Fail point 需求在我们的集成测试中,都是简单的构建一个 KV 实例,然后发送请求,检查返回值和状态的改变。这样的测试可以较为完整地测试功能,但是对于一些需要精细化控制的测试就鞭长莫及了。我们当然可以通过 mock 网络层提供网络的精细模拟控制,但是对于诸如磁盘 IO、系统调度等方面的控制就没办法做到了。同时,在分布式系统中时序的关系是非常关键的,可能两个操作的执行顺行相反,就导致了迥然不同的结果。尤其对于数据库来说,保证数据的一致性是至关重要的,因此需要去做一些相关的测试。基于以上原因,我们就需要使用 fail point 来复现一些 corner case,比如模拟数据落盘特别慢、raftstore 繁忙、特殊的操作处理顺序、错误 panic 等等。基本用法示例在详细介绍之前,先举一个简单的例子给大家一个直观的认识。还是那个老生常谈的 Hello World:#[macro_use]extern crate fail;fn say_hello() { fail_point!(“before_print”); println!(“Hello World~”);}fn main() { say_hello(); fail::cfg(“before_print”, “panic”); say_hello();}运行结果如下:Hello World~thread ‘main’ panicked at ‘failpoint before_print panic’ …可以看到最终只打印出一个 Hello World~,而在打印第二个之前就 panic 了。这是因为我们在第一次打印完后才指定了这个 fail point 行为是 panic,因此第一次在 fail point 不做任何事情之后正常输出,而第二次在执行到 fail point 时就会根据配置的行为 panic 掉!Fail point 行为当然 fail point 不仅仅能注入 panic,还可以是其他的操作,并且可以按照一定的概率出现。描述行为的格式如下:[<pct>%][<cnt>*]<type>[(args…)][-><more terms>]pct:行为被执行时有百分之 pct 的机率触发cnt:行为总共能被触发的次数type:行为类型off:不做任何事return(arg):提前返回,需要 fail point 定义时指定 expr,arg 会作为字符串传给 expr 计算返回值sleep(arg):使当前线程睡眠 arg 毫秒panic(arg):使当前线程崩溃,崩溃消息为 argprint(arg):打印出 argpause:暂停当前线程,直到该 fail point 设置为其他行为为止yield:使当前线程放弃剩余时间片delay(arg):和 sleep 类似,但是让 CPU 空转 arg 毫秒args:行为的参数比如我们想在 before_print 处先 sleep 1s 然后有 1% 的机率 panic,那么就可以这么写:“sleep(1000)->1%panic"定义 fail point只需要使用宏 fail_point! 就可以在相应代码中提前定义好 fail point,而具体的行为在之后动态注入。fail_point!(“failpoint_name”);fail_point!(“failpoint_name”, || { // 指定生成自定义返回值的闭包,只有当 fail point 的行为为 return 时,才会调用该闭包并返回结果 return Error});fail_point!(“failpoint_name”, a == b, || { // 当满足条件时,fail point 才被触发 return Error})动态注入环境变量通过设置环境变量指定相应 fail point 的行为:FAILPOINTS="<failpoint_name1>=<action>;<failpoint_name2>=<action>;…“注意,在实际运行的代码需要先使用 fail::setup() 以环境变量去设置相应 fail point,否则 FAILPOINTS 并不会起作用。#[macro_use]extern crate fail;fn main() { fail::setup(); // 初始化 fail point 设置 do_fallible_work(); fail::teardown(); // 清除所有 fail point 设置,并且恢复所有被 fail point 暂停的线程}代码控制不同于环境变量方式,代码控制更加灵活,可以在程序中根据情况动态调整 fail point 的行为。这种方式主要应用于集成测试,以此可以很轻松地构建出各种异常情况。fail::cfg(“failpoint_name”, “actions”); // 设置相应的 fail point 的行为fail::remove(“failpoint_name”); // 解除相应的 fail point 的行为内部实现以下我们将以 fail-rs v0.2.1 版本代码为基础,从 API 出发来看看其背后的具体实现。fail-rs 的实现非常简单,总的来说,就是内部维护了一个全局 map,其保存着相应 fail point 所对应的行为。当程序执行到某个 fail point 时,获取并执行该全局 map 中所保存的相应的行为。全局 map 其具体定义在 FailPointRegistry。struct FailPointRegistry { registry: RwLock<HashMap<String, Arc<FailPoint>>>,}其中 FailPoint 的定义如下:struct FailPoint { pause: Mutex<bool>, pause_notifier: Condvar, actions: RwLock<Vec<Action>>, actions_str: RwLock<String>,}pause 和 pause_notifier 是用于实现线程的暂停和恢复,感兴趣的同学可以去看看代码,太过细节在此不展开了;actions_str 保存着描述行为的字符串,用于输出;而 actions 就是保存着 failpoint 的行为,包括概率、次数、以及具体行为。Action 实现了 FromStr 的 trait,可以将满足格式要求的字符串转换成 Action。这样各个 API 的操作也就显而易见了,实际上就是对于这个全局 map 的增删查改:fail::setup() 读取环境变量 FAILPOINTS 的值,以 ; 分割,解析出多个 failpoint name 和相应的 actions 并保存在 registry 中。fail::teardown() 设置 registry 中所有 fail point 对应的 actions 为空。fail::cfg(name, actions) 将 name 和对应解析出的 actions 保存在 registry 中。fail::remove(name) 设置 registry 中 name 对应的 actions 为空。而代码到执行到 fail point 的时候到底发生了什么呢,我们可以展开 fail_point! 宏定义看一下:macro_rules! fail_point { ($name:expr) => {{ $crate::eval($name, |_| { panic!(“Return is not supported for the fail point "{}"”, $name); }); }}; ($name:expr, $e:expr) => {{ if let Some(res) = $crate::eval($name, $e) { return res; } }}; ($name:expr, $cond:expr, $e:expr) => {{ if $cond { fail_point!($name, $e); } }};}现在一切都变得豁然开朗了,实际上就是对于 eval 函数的调用,当函数返回值为 Some 时则提前返回。而 eval 就是从全局 map 中获取相应的行为,在 p.eval(name) 中执行相应的动作,比如输出、等待亦或者 panic。而对于 return 行为的情况会特殊一些,在 p.eval(name) 中并不做实际的动作,而是返回 Some(arg) 并通过 .map(f) 传参给闭包产生自定义的返回值。pub fn eval<R, F: FnOnce(Option<String>) -> R>(name: &str, f: F) -> Option<R> { let p = { let registry = REGISTRY.registry.read().unwrap(); match registry.get(name) { None => return None, Some(p) => p.clone(), } }; p.eval(name).map(f)}小结至此,关于 fail-rs 背后的秘密也就清清楚楚了。关于在 TiKV 中使用 fail point 的测试详见 github.com/tikv/tikv/tree/master/tests/failpoints,大家感兴趣可以看看在 TiKV 中是如何来构建异常情况的。同时,fail-rs 计划支持 HTTP API,欢迎感兴趣的小伙伴提交 PR。 ...

April 1, 2019 · 2 min · jiezi

【RUST官方语言中文翻译】简介

介绍欢迎阅读Rust编程语言,这是一本有关Rust的入门书籍。Rust语言将会帮助你更快,更好的编写可信赖的软件。在编程语言的设计中,高级语言的易读性和低级语言的易控制经常是一个矛盾点。Rust会试图解决这个冲突。通过在强大的技术能力和开发友好性之间取得某种平衡,Rust会让你在编写控制底层细节(如内存相关)时,避免常见的一些陷阱Rust适合哪些人Rust对于很多人来说都有很多理由适合。让我们看下一些其中最重要的人群。开发者团队Rust是被设计成一种在大的有各种系统能力级别的开发者团队中协作的创造性工具。低级语言很容易造成一些微妙的bug,而这些bug在其他语言中,往往只能通过广泛的测试和让经验丰富的开发者进行代码检查才能发现。在Rust中,编译器承担起‘守门员’这个角色,当代码中有这些难以捉摸的bug,包括并发的bug时,编译器会拒绝编译。通过编译器的帮助,团队可以将更多的时间花在编程逻辑而不是追踪bug上。Rust同时也给系统编程带来了一些现代的开发者工具:Cargo, 内建的依赖管理器和构建工具,使得添加依赖,编译,管理依赖都十分方便,并且能与Rust生态保持一致。Rustfmt, 确保了不同开发者的代码风格一致性Rust语言服务器,增强了整合开发环境(IDE)对于代码编译和出错信息的提示。通过这些以及其他Rust生态中的工具,使得开发者能够在开发系统层级的代码时极大得提高了。学生Rust适合那些对系统编程感兴趣的学生和人们。通过Rust,很多人学到了一些有关系统操作系统编程的知识点。Rust社区十分欢迎并乐于回答学生问题。Rust团队希望通过多种方式,比如这本书,帮助更多人理解系统编程的概念,尤其是那些刚学习编程的人们。机构成千上百或大或小的公司,在生产环境中使用Rust去实现不同的任务。包括命令行工具,web服务,开发工具,音频视频的分析与解码,加密货币,生物学,搜索引擎,物联网应用,机器学习,甚至还有火狐游览器的主要组成部分。开源开发者Rust被用来服务那些使用Rust来构建Rust编程语言,社区,开发工具,第三方库。我们欢迎你为Rust语言做出贡献。重视效率和稳定性的人群Rust适合那些重视编程语言的效率和稳定性的人们。所谓的速度,指的是你可以用Rust创建程序的速度以及你编写Rust的速度。Rust编译器的检测通过额外的特性和重构来确保稳定性。这跟那些语言中没有这些检查,让程序员害怕修改的脆弱遗留代码形成了一致鲜明的对比。通过零代价的抽象,高层次特性,Rust编译低层级速度就跟人工写的代码一样,Rust确保代码安全的同时也确保代码足够快。Rust希望也能同时支持其他用户,上面提到的用户只是受益最大的群体。总体来说,Rust最大的目标就是消除程序员几十年下来,在安全,性能,经济性之间的权衡。欢迎尝试Rust看看它的选择是否适合你。谁适合阅读本书本书假定你曾使用过其他的编程语言,但是不假定具体的哪一种。我们尽可能的使本书的材料足够丰富以使任何编程背景的人们都可以理解它们。我们不会在诸如什么是编程,如何考虑编程这种问题上花费太多时间。如果你是一个刚接触编程的人士,你最好阅读一本详细介绍了编程的书籍。如何使用本书大体上,这本书假定你会按顺序从头往后阅读本书。后面的章节都是以前面的章节中的概念为基础构建的,而早期的章节不会太深入某个主题;我们往往会在后面的章节中回顾某个主题。本书中有两种类型都章节:概念型章节和项目型章节。在概念型章节中,你将会了解到Rust的某个方面,在项目章节中,我们运用已经学到的知识,一起构建一个小程序。第2,12,20章是项目章节,其他都是概念型章节。第一章说明了如何安装Rust,如何编写一个Hello world程序以及如何使用Cargo—Rust的包管理器以及构建工具。第二章是一个Rust的动手指南,里面包含了一些高层级的概念,这些概念会在稍后的章节中提供更详细的信息。如果你希望能马上动手干,那第二章是一个比较好的突破口。刚开始的时候,你可能想跳过第三章,这一章主要涵盖的是Rust和其他编程语言的相似特性,同时,你会直接深入第四章,这是有关Rust的所有权系统的。然而,如果你是一个强迫症患者,习惯在学习下一章之前学习到所有详细的知识点,你可以跳过第二章并直接进入第三章,然后在开发项目并使用一个具体知识点时,重新回到第二章。第五章讨论了结构体和方法,第六章涵盖了枚举,match表达式和 if let流程控制。之后你可以使用结构体和枚举定义自己的Rust类型。第七章中,你会了解到关于Rust的模块系统,以及组织你的代码和公共的应用程序接口的隐私规则。第八章讨论了一些标准库提供的一些常见的数据集合,比如向量,字符串,哈希表。第九章则会探讨Rust的错误处理理念和技巧。第十章会深入了解范型,特征和生命周期,这些使得你有能力能构建应用到多种类型的代码。第十一章都是有关测试的,即使有Rust类型保证,测试依然是必要的去确保你程序的逻辑是正确的。在第十二章,我们会构建一个用来在文件中搜索文本的工具,它是grep命令行工具的一个子。为了得到这个目标,我们会使用很多在之前章节讨论的内容和概念。第十三章会探讨闭包和迭代–两个Rust从函数式编程继承过来的特性。在第十四章,我们会更加深入了解Cargo并且讨论下将你的代码库分享给他人的最佳实践。第十五章,主要讨论标准库提供的智能指针以及实现其功能的特征。第十六章,我们查看下并发编程时的不同模型以及讨论下Rust如何帮助你在多线程时如何无风险的编程。第十七章会聚焦于Rust的一些编程习惯与你之前已经熟悉的面向对象编程中一些规则的区别。第十八章是关于模式好模式匹配的参考,在Rust中,模式是一种强大的表现灵感的方式。第十九是一个包括很多令人感兴趣的大杂烩,如非安全Rust,宏以及生命周期,特征,类型,函数,闭包等。第二十章,我们会完成一个实现了低层级多线程的网络服务器。最后,附录中用引用的形式包含了一些有关Rust语言的有用信息。 附录A包含了Rust的关键字,附录B包含了Rust的操作符和符号,附录C包含了标准库提供的特征推导, 附录D包含了一些有用的开发者工具,附录E解释了Rust版本信息。可以采用任何顺序阅读本书,如果你想跳过某些内容,那就直接跳过。只要在你碰到一些疑惑时在回过头来查看早些章节即可。 你可以做任何想做的事。学习Rust最重要的部分之一就是学习如何阅读编译器展现的错误信息:这些会引导你完成能运行的代码。因此, 我们会提供很多不通过编译的示例代码,编译器会展现各个场景下的错误信息。记住,当你使用一个某个示例,它很可能是编译不通过!确保你阅读了周围的文本以确认你正在使用的示例是否出错。 Ferris会帮助你区分那些不能运行的代码的含义。绝大多数场景中, 我们都会引导你将编译不通过的代码改正为通过编译的代码。源码:本书中使用的源码可以在这里获取 Github

March 29, 2019 · 1 min · jiezi

TiKV 源码解析系列文章(四)Prometheus(下)

作者: Breezewish本文为 TiKV 源码解析系列的第四篇,接上篇继续为大家介绍 [rust-prometheus]。上篇 主要介绍了基础知识以及最基本的几个指标的内部工作机制,本篇会进一步介绍更多高级功能的实现原理。与上篇一样,以下内部实现都基于本文发布时最新的 rust-prometheus 0.5 版本代码,目前我们正在开发 1.0 版本,API 设计上会进行一些简化,实现上出于效率考虑也会和这里讲解的略微有一些出入,因此请读者注意甄别。指标向量(Metric Vector)Metric Vector 用于支持带 Label 的指标。由于各种指标都可以带上 Label,因此 Metric Vector 本身实现为了一种泛型结构体,[Counter]、[Gauge] 和 [Histogram] 在这之上实现了 [CounterVec]、[GaugeVec] 和 [HistogramVec]。Metric Vector 主要实现位于 src/vec.rs。以 [HistogramVec] 为例,调用 [HistogramVec::with_label_values] 可获得一个 [Histogram] 实例,而 [HistogramVec] 定义为:pub type HistogramVec = MetricVec<HistogramVecBuilder>;pub struct MetricVec<T: MetricVecBuilder> { pub(crate) v: Arc<MetricVecCore<T>>,}impl<T: MetricVecBuilder> MetricVec<T> { pub fn with_label_values(&self, vals: &[&str]) -> T::M { self.get_metric_with_label_values(vals).unwrap() }}因此 [HistogramVec::with_label_values] 的核心逻辑其实在 MetricVecCore::get_metric_with_label_values。这么做的原因是为了让 MetricVec 是一个线程安全、可以被全局共享但又不会在共享的时候具有很大开销的结构,因此将内部逻辑实现在 MetricVecCore,外层(即在 MetricVec)套一个 Arc<T> 后再提供给用户。进一步可以观察 MetricVecCore 的实现,其核心逻辑如下:pub trait MetricVecBuilder: Send + Sync + Clone { type M: Metric; type P: Describer + Sync + Send + Clone; fn build(&self, &Self::P, &[&str]) -> Result<Self::M>;}pub(crate) struct MetricVecCore<T: MetricVecBuilder> { pub children: RwLock<HashMap<u64, T::M>>, // Some fields are omitted.}impl<T: MetricVecBuilder> MetricVecCore<T> { // Some functions are omitted. pub fn get_metric_with_label_values(&self, vals: &[&str]) -> Result<T::M> { let h = self.hash_label_values(vals)?; if let Some(metric) = self.children.read().get(&h).cloned() { return Ok(metric); } self.get_or_create_metric(h, vals) } pub(crate) fn hash_label_values(&self, vals: &[&str]) -> Result<u64> { if vals.len() != self.desc.variable_labels.len() { return Err(Error::InconsistentCardinality( self.desc.variable_labels.len(), vals.len(), )); } let mut h = FnvHasher::default(); for val in vals { h.write(val.as_bytes()); } Ok(h.finish()) } fn get_or_create_metric(&self, hash: u64, label_values: &[&str]) -> Result<T::M> { let mut children = self.children.write(); // Check exist first. if let Some(metric) = children.get(&hash).cloned() { return Ok(metric); } let metric = self.new_metric.build(&self.opts, label_values)?; children.insert(hash, metric.clone()); Ok(metric) }}现在看代码就很简单了,它首先会依据所有 Label Values 构造一个 Hash,接下来用这个 Hash 在 RwLock<HashMap<u64, T::M>> 中查找,如果找到了,说明给定的这个 Label Values 之前已经出现过、相应的 Metric 指标结构体已经初始化过,因此直接返回对应的实例;如果不存在,则要利用给定的 [MetricVecBuilder] 构造新的指标加入哈希表,并返回这个新的指标。由上述代码可见,为了在线程安全的条件下实现 Metric Vector 各个 Label Values 具有独立的时间序列,Metric Vector 内部采用了 RwLock 进行同步,也就是说 with_label_values() 及类似函数内部是具有锁的。这在多线程环境下会有一定的效率影响,不过因为大部分情况下都是读锁,因此影响不大。当然,还可以发现其实给定 Label Values 之后调用 with_label_values() 得到的指标实例是可以被缓存起来的,只访问缓存起来的这个指标实例是不会有任何同步开销的,也绕开了计算哈希值等比较占 CPU 的操作。基于这个思想,就有了 Static Metrics,读者可以在本文的后半部分了解 Static Metrics 的详细情况。另外读者也可以发现,Label Values 的取值应当是一个有限的、封闭的小集合,不应该是一个开放的或取值空间很大的集合,因为每一个值都会对应一个内存中指标实例,并且不会被释放。例如 HTTP Method 是一个很好的 Label,因为它只可能是 GET / POST / PUT / DELETE 等;而 Client Address 则很多情况下并不适合作为 Label,因为它是一个开放的集合,或者有非常巨大的取值空间,如果将它作为 Label 很可能会有容易 OOM 的风险。这个风险在 Prometheus 官方文档中也明确指出了。整型指标(Integer Metric)在讲解 Counter / Gauge 的实现时我们提到,[rust-prometheus] 使用 CAS 操作实现 [AtomicF64] 中的原子递增和递减,如果改用 atomic fetch-and-add 操作则一般可以取得更高效率。考虑到大部分情况下指标都可以是整数而不需要是小数,例如对于简单的次数计数器来说它只可能是整数,因此 [rust-prometheus] 额外地提供了整型指标,允许用户自由地选择,针对整数指标情况提供更高的效率。为了增强代码的复用,[rust-prometheus] 实际上采用了泛型来实现 [Counter] 和 [Gauge]。通过对不同的 Atomic(如 [AtomicF64]、[AtomicI64])进行泛化,就可以采用同一份代码实现整数的指标和(传统的)浮点数指标。[Atomic] trait 定义如下(src/atomic64/mod.rs):pub trait Atomic: Send + Sync { /// The numeric type associated with this atomic. type T: Number; /// Create a new atomic value. fn new(val: Self::T) -> Self; /// Set the value to the provided value. fn set(&self, val: Self::T); /// Get the value. fn get(&self) -> Self::T; /// Increment the value by a given amount. fn inc_by(&self, delta: Self::T); /// Decrement the value by a given amount. fn dec_by(&self, delta: Self::T);}原生的 [AtomicU64]、[AtomicI64] 及我们自行实现的 [AtomicF64] 都实现了 [Atomic] trait。进而,[Counter] 和 [Gauge] 都可以利用上 [Atomic] trait:pub struct Value<P: Atomic> { pub val: P, // Some fields are omitted.}pub struct GenericCounter<P: Atomic> { v: Arc<Value<P>>,}pub type Counter = GenericCounter<AtomicF64>;pub type IntCounter = GenericCounter<AtomicI64>;本地指标(Local Metrics)由前面这些源码解析可以知道,指标内部的实现是原子变量,用于支持线程安全的并发更新,但这在需要频繁更新指标的场景下相比简单地更新本地变量仍然具有显著的开销(大约有 10 倍的差距)。为了进一步优化、支持高效率的指标更新操作,[rust-prometheus] 提供了 Local Metrics 功能。rust-prometheus 中 Counter 和 Histogram 指标支持 local() 函数,该函数会返回一个该指标的本地实例。本地实例是一个非线程安全的实例,不能多个线程共享。例如,[Histogram::local()] 会返回 [LocalHistogram]。由于 Local Metrics 使用是本地变量,开销极小,因此可以放心地频繁更新 Local Metrics。用户只需定期调用 Local Metrics 的 flush() 函数将其数据定期同步到全局指标即可。一般来说 Prometheus 收集数据的间隔是 15s 到 1 分钟左右(由用户自行配置),因此即使是以 1s 为间隔进行 flush() 精度也足够了。普通的全局指标使用流程如下图所示,多个线程直接利用原子操作更新全局指标:本地指标使用流程如下图所示,每个要用到该指标的线程都保存一份本地指标。更新本地指标操作开销很小,可以在频繁的操作中使用。随后,只需再定期将这个本地指标 flush 到全局指标,就能使得指标的更新操作真正生效。TiKV 中大量运用了本地指标提升性能。例如,TiKV 的线程池一般都提供 Context 变量,Context 中存储了本地指标。线程池上运行的任务都能访问到一个和当前 worker thread 绑定的 Context,因此它们都可以安全地更新 Context 中的这些本地指标。最后,线程池一般提供 tick() 函数,允许以一定间隔触发任务,在 tick() 中 TiKV 会对这些 Context 中的本地指标进行 flush()。Local Counter[Counter] 的本地指标 [LocalCounter] 实现很简单,它是一个包含了计数器的结构体,该结构体提供了与 [Counter] 一致的接口方便用户使用。该结构体额外提供了 flush(),将保存的计数器的值作为增量值更新到全局指标:pub struct GenericLocalCounter<P: Atomic> { counter: GenericCounter<P>, val: P::T,}pub type LocalCounter = GenericLocalCounter<AtomicF64>;pub type LocalIntCounter = GenericLocalCounter<AtomicI64>;impl<P: Atomic> GenericLocalCounter<P> { // Some functions are omitted. pub fn flush(&mut self) { if self.val == P::T::from_i64(0) { return; } self.counter.inc_by(self.val); self.val = P::T::from_i64(0); }}Local Histogram由于 [Histogram] 本质也是对各种计数器进行累加操作,因此 [LocalHistogram] 的实现也很类似,例如 observe(x) 的实现与 [Histogram] 如出一辙,除了它不是原子操作;flush() 也是将所有值累加到全局指标上去:pub struct LocalHistogramCore { histogram: Histogram, counts: Vec<u64>, count: u64, sum: f64,}impl LocalHistogramCore { // Some functions are omitted. pub fn observe(&mut self, v: f64) { // Try find the bucket. let mut iter = self .histogram .core .upper_bounds .iter() .enumerate() .filter(|&(_, f)| v <= *f); if let Some((i, _)) = iter.next() { self.counts[i] += 1; } self.count += 1; self.sum += v; } pub fn flush(&mut self) { // No cached metric, return. if self.count == 0 { return; } { let h = &self.histogram; for (i, v) in self.counts.iter().enumerate() { if *v > 0 { h.core.counts[i].inc_by(*v); } } h.core.count.inc_by(self.count); h.core.sum.inc_by(self.sum); } self.clear(); }}静态指标(Static Metrics)之前解释过,对于 Metric Vector 来说,由于每一个 Label Values 取值都是独立的指标实例,因此为了线程安全实现上采用了 HashMap + RwLock。为了提升效率,可以将 with_label_values 访问获得的指标保存下来,以后直接访问。另外使用姿势正确的话,Label Values 取值是一个有限的、确定的、小的集合,甚至大多数情况下在编译期就知道取值内容(例如 HTTP Method)。综上,我们可以直接写代码将各种已知的 Label Values 提前保存下来,之后可以以静态的方式访问,这就是静态指标。以 TiKV 为例,有 Contributor 为 TiKV 提过这个 PR:#2765 server: precreate some labal metrics。这个 PR 改进了 TiKV 中统计各种 gRPC 接口消息次数的指标,由于 gRPC 接口是固定的、已知的,因此可以提前将它们缓存起来:struct Metrics { kv_get: Histogram, kv_scan: Histogram, kv_prewrite: Histogram, kv_commit: Histogram, // …}impl Metrics { fn new() -> Metrics { Metrics { kv_get: GRPC_MSG_HISTOGRAM_VEC.with_label_values(&[“kv_get”]), kv_scan: GRPC_MSG_HISTOGRAM_VEC.with_label_values(&[“kv_scan”]), kv_prewrite: GRPC_MSG_HISTOGRAM_VEC.with_label_values(&[“kv_prewrite”]), kv_commit: GRPC_MSG_HISTOGRAM_VEC.with_label_values(&[“kv_commit”]), // … } }}使用的时候也很简单,直接访问即可:@@ -102,10 +155,8 @@ fn make_callback<T: Debug + Send + ‘static>() -> (Box<FnBox(T) + Send>, oneshot:impl<T: RaftStoreRouter + ‘static> tikvpb_grpc::Tikv for Service<T> { fn kv_get(&self, ctx: RpcContext, mut req: GetRequest, sink: UnarySink<GetResponse>) {- let label = “kv_get”;- let timer = GRPC_MSG_HISTOGRAM_VEC- .with_label_values(&[label])- .start_coarse_timer();+ const LABEL: &str = “kv_get”;+ let timer = self.metrics.kv_get.start_coarse_timer(); let (cb, future) = make_callback(); let res = self.storage.async_get(这样一个简单的优化可以为 TiKV 提升 7% 的 Raw Get 效率,可以说是很超值了(主要原因是 Raw Get 本身开销极小,因此在指标上花费的时间就显得有一些显著了)。但这个优化方案其实还有一些问题:代码繁琐,有大量重复的、或满足某些 pattern 的代码;如果还有另一个 Label 维度,那么需要维护的字段数量就会急剧膨胀(因为每一种值的组合都需要分配一个字段)。为了解决以上两个问题,[rust-prometheus] 提供了 Static Metric 宏。例如对于刚才的 TiKV 改进 PR #2765 来说,使用 Static Metric 宏可以简化为:make_static_metric! { pub struct GrpcMsgHistogram: Histogram { “type” => { kv_get, kv_scan, kv_prewrite, kv_commit, // … }, }}let metrics = GrpcMsgHistogram::from(GRPC_MSG_HISTOGRAM_VEC);// Usage:metrics.kv_get.start_coarse_timer();可以看到,使用宏之后,需要维护的繁琐的代码量大大减少了。这个宏也能正常地支持多个 Label 同时存在的情况。限于篇幅,这里就不具体讲解这个宏是如何写的了,感兴趣的同学可以观看我司同学最近在 FOSDEM 2019 上的技术分享 视频(进度条 19:54 开始介绍 Static Metrics)和 Slide,里面详细地介绍了如何从零开始写出一个这样的宏(的简化版本)。 ...

March 14, 2019 · 4 min · jiezi

k8s与configmap--安利configmap-reload组件

前言在kubernetes集群内,当ConfigMap以volume形式挂载到pod内时,更新ConfigMap,kubernetes会自动同步被挂载到pod内的文件内容。当然并不是更改立即生效的,大约是需要10S钟后,才会生效。实际生产使用案例中,假如你的应用具备hot reload 功能, 这时可以增加一些监测配置文件变更的脚本,然后reload对应服务。比如prometheus。今天就给大家介绍一个configmap-reload 组件。configmap-reloadconfigmap-reload 采用rust语言实现,作为主业务容器的sidercar,主要用于k8s当中监听configmap的变化,待变化后通过http接口的方式通知主业务。在资源消耗上,更小。具体如下:[root@ip-172-xx-xx-10 src]# kubectl top podsNAME CPU(cores) MEMORY(bytes)configmap-reload-6bbbb8b45b-7zg2x 0m 1Mi输入参数可以通过configmap-reload -h 获取:configmap-reload 0.1.0gaohj <gaohj2015@yeah.net>USAGE: configmap-reload [OPTIONS]FLAGS: -h, –help Prints help information -V, –version Prints version informationOPTIONS: -l, –log_level <LOG_LEVEL> log level: error|warn|info|debug|trace [default: info] -p, –path <VOLUME_PATH> the config map volume directory to watch for updates [default: ] -m, –webhook_method <WEBHOOK_METHOD> the HTTP method url to use to send the webhook: GET|POST [default: POST] -c, –webhook_status_code <WEBHOOK_STATUS_CODE> the HTTP status code indicating successful triggering of reload [default: 200] -u, –webhook_url <WEBHOOK_URL> the HTTP method url to use to send the webhook [default: ] 示例使用:—apiVersion: v1kind: ConfigMapmetadata: labels: app: configmap-reload name: configmap-reload-cmdata: test.ini: |- key: a—kind: DeploymentapiVersion: apps/v1metadata: name: configmap-reload labels: app: configmap-reloadspec: replicas: 1 selector: matchLabels: app: configmap-reload template: metadata: labels: app: configmap-reload spec: volumes: - name: config configMap: name: configmap-reload-cm containers: - name: configmap-reload image: ‘iyacontrol/configmap-reload:v0.1’ command: - configmap-reload args: - -l - debug - -p - /etc/test/ - -c - ‘200’ - -u - https://www.baidu.com volumeMounts: - name: config mountPath: /etc/test/ imagePullPolicy: Always—总结大家直接可以拉取 dockerhub 中的镜像。当然仓库中已经提供了Dockerfile文件,FROM clux/muslrust:stable as builderWORKDIR /configmap-reloadCOPY ./ ./ARG use_mirrorRUN if [ $use_mirror ]; then \ mkdir -p $HOME/.cargo; \ mv -f ./docker/cargo_config $HOME/.cargo/config; \ fiRUN cargo build –release#####################################FROM alpine:latest as prodRUN apk add –no-cache ca-certificates COPY –from=0 /configmap-reload/target/x86_64-unknown-linux-musl/release/configmap-reload /usr/bin/configmap-reloadRUN chmod +x /usr/bin/configmap-reloadENTRYPOINT [“configmap-reload”]大家可以自己打镜像,然后push到自己的仓库中。 ...

March 10, 2019 · 1 min · jiezi

与 Rust 社区亲密接触!首届 RustCon Asia 来了

首先抢票:www.huodongxing.com/event/64794…大会官网:rustcon.asia2019 年 4 月 20 日在北京望京凯悦,在秘猿科技与 PingCAP 携手下,我们开始即将开启第一场 RustCon Asia。大会为期 4 天,1.5 天主题演讲,2 天动手研讨 workshop。Rust 是一门比较现代的,吸收了最近几十年在语言设计和编译器方面的优秀精华的语言。我们非常喜欢他的设计,尤其是 Rust 在工程方面展现出的强大优势:Performance 高性能!Reliability 可靠!Productivity 效率!Rust 是被生产环境验证并且取得成功的语言,秘猿科技基于 Rust 开发的系统在国内最顶级的金融系统中跑的一些项目,已经充分验证了 Rust 的能力。另外,Rust 社区是一个我们最推崇的那种包容、开放的社区,Rust 的每一项改进,都是通过 RFC 去讨论、去达成共识再实现。Rust 这种基于 RFC 的机制给了这门语言极为旺盛的生命力和开放度,我们信仰开放的文化更有机会成功,所以我们在做自己的项目的时候,也借鉴了 RFC 机制。Rust 海外社区非常活跃,我们一直在吸收,现在是合适的时间去反哺社区。从去年开始,我们趁着各种机会开始投入 Rust 社区。我们开始了每月一次的 Rust Dev Sharing,第一场的直播达到了 2000 人同时在线;第二次,Bilibili 的雪松老师在工作日从上海跑来杭州加入了分享。我们感受到了 Rust 社区的激情,看到国内越来越多的 Rust 项目,也笃定 Rust 是未来。去年 11 月,秘猿科技赞助并参与了罗马的 RustFest 为期 4 天的大会,会上我们碰到了 Rust team 的 Aaron、Pascal、Steve、Florian,社区活跃开发者,当然还有 PingCAP 的伙伴 Ana(Hoverbear)。即便有着跨文化沟通的障碍存在,大家对亚洲的 Rust 大会都表达出非常地期待,以至于在活动官网还没上线的时候,欧美的 Rust 社区就已经知晓并传播中国 Rust 大会的声音。Rust Language 的 Twitter @rustlang 还在除夕夜发了一条「猪年大吉」的推文!经过春节假期,我们在 2 月 15 日上线 RustCon Asia 官网(http://rustcon.asia),接下来 2 周时间我们的 CFP(Call For Proposals) 流程共计收到中英文 proposal 50 份。社区小伙伴热情帮推,除了各种群、朋友圈、知乎、Twitter 帮扩散,《Rust 编程之道》作者汉东老师将大会信息发到了 Rust 的每日订阅频道,有国外来不了的小伙伴购买了 donation 票,作为对大会的支持。与其说这是我们组织的大会,不如说是社区驱动的结果。在此也特别感谢帮助推广 RustCon Asia 信息的社区合作伙伴我们在此再次介绍一下大会详情安排: 4.20-21 为一天半的 keynote 演讲——要从 50 份 proposal 里面评选出 14 位优秀讲师!不用担心语言问题,我们有同传!4.22-23 为两天的 workshop,3 个房间同时进行——你可以有更丰富更自由的选择!这是一场属于社区的大会,除了正经的大会安排,我们也强烈建议现场及时行乐,各种组小队约饭、约咖啡、约酒,其它约什么我们不管(邪恶微笑)。毕竟一年仅此一次!扩散起来,邀请更多的小伙伴一起来嗨!Rust 爱好者、开源爱好者、技术社区贡献者、来旁观大家怎么嗨的,就是你们了!Rust 可以让人重新爱上编程!请肆无忌惮地炫耀,Rust 社区真的很棒。另外八卦一下大会门票 399,包含大会的 350 自助午餐!限额四百张,购票进行中~ ...

March 6, 2019 · 1 min · jiezi