OWenT's blog
  • Introduction
  • About Me
  • 2020
    • 近期对libatapp的一些优化调整(增加服务发现和连接管理,支持yaml等)
    • xresloader转表工具链增加了一些新功能(map,oneof支持,输出矩阵,基于模板引擎的加载代码生成等)
    • 在游戏服务器中使用分布式事务
    • libcopp接入C++20 Coroutine和一些过渡期的设计
    • libatbus 的大幅优化
    • nftables初体验
    • 容器配置开发环境小计
  • 2019
    • PALM Tree - 适合多核并发架构的B+树 - 论文阅读小记
    • 跨平台协程库 - libcopp 简介
    • C++20 Coroutine 性能测试 (附带和libcopp/libco/libgo/goroutine/linux ucontext对比)
    • 尝鲜Github Action
    • 一些xresloader(转表工具)的改进
    • protobuf、flatbuffer、msgpack 针对小数据包的简单对比
    • 协程框架(libcopp) 小幅优化
    • Excel转表工具(xresloader) 增加protobuf插件功能和集成 UnrealEngine 支持
    • Anna(支持任意扩展和超高性能的KV数据库系统)阅读笔记
    • C++20 Coroutine
    • libcopp merge boost.context 1.69.0
    • Google去中心化分布式系统论文三件套(Percolator、Spanner、F1)读后感
    • Rust玩具-企业微信机器人通用服务
  • 2018
    • 使用ELK辅助监控开发测试环境服务质量和问题定位
    • Webpack+vue+boostrap+ejs构建Web版GM工具
    • 2018年的新通用伪随机数算法(xoshiro / xoroshiro)的C++(head only)实现
    • Rust的第二次接触-写个小服务器程序
    • 理解和适配AEAD加密套件
    • atsf4g-co的进化:协程框架v2、对象路由系统和一些其他细节优化
    • 协程框架(libcopp)v2优化、自适应栈池和同类库的Benchmark对比
    • 可执行文件压缩
    • 初识Rust
    • 使用restructedtext编写xresloader文档
    • atframework的etcd模块化重构
    • C++的backtrace
  • 2017
    • ECDH椭圆双曲线(比DH快10倍的密钥交换)算法简介和封装
    • protobuf-net的动态Message实现
    • pbc的proto3接入
    • atgateway内置协议流程优化-加密、算法协商和ECDH
    • 整理一波软件源镜像同步工具+DevOps工具
    • Blog切换到Hugo
    • libcopp v2的第一波优化完成
    • libcopp(v2) vs goroutine性能测试
    • libcopp的线程安全、栈池和merge boost.context 1.64.0
    • GCC 7和LLVM+Clang+libc++abi 4.0的构建脚本
    • libatbus的几个藏得很深的bug
    • 用cmake交叉编译到iOS和Android
    • 开源项目得一些小维护
    • atapp的c binding和c#适配
    • 对象路由系统设计
    • 2016年总结
    • 近期的一个协程流程BUG
  • 2016
    • 重写了llvm+clang+libc++和libc++abi的构建脚本
    • atsf4g完整游戏工程示例
    • atframework基本框架已经完成
    • 游戏服务器的不停服更新
    • 对atbus的小数据包的优化
    • Android和IOS的TLS问题
    • pbc的一个陈年老BUG
    • boost.context-1.61版本的设计模型变化
    • 接入letsencrypt+全面启用HTTP/2
    • 理解Raft算法
    • libatbus基本功能及单元测试终于写完啦
    • 博客文章和文档迁移到gitbook
  • 2015
    • 博客文章和文档迁移到gitbook
    • 给客户端写得LRU缓存
    • 近期活动比较零散
    • 关于BUS通信系统的一些思考(三)
    • 针对Java JIT的优化(转表工具:xresloader)
    • libcopp更新 (merge boost 1.59 context)
    • 小记最近踩得两个C++坑
    • Redis全异步(HA)Driver设计稿
    • Vim常用命令
    • 关于firewalld和systemd的一些命令速记
    • Jenkins(hudson)插件记录
    • 我们的Lua类绑定机制
    • LLVM+Clang+Libcxx+Libcxxabi(3.6)工具链编译(完成自举编译)
    • 回顾2014
    • Android NDK undefined reference to ___tls_get_addr 错误
    • gitlab腾讯企业邮箱配置
  • 2014
    • 回顾2013
    • C++11动态模板参数和type_traits
    • C++又一坑:动态链接库中的全局变量
    • tolua++内存释放坑
    • [转]类似github的框架
    • Lua性能分析
    • 集成Qt Webkit 到cocos2d-x
    • Gitlab环境搭建小计
    • 近期研究VPN的一些记录(OpenVPN,pptp,l2tp)
    • LLVM + Clang + Libcxx + Libcxxabi 工具链编译
    • 关于BUS通信系统的一些思考(二)
    • 关于BUS通信系统的一些思考(一)
    • [libiniloader] Project
    • 记录一些在线编辑器
    • [WP Code Highlight.js] Project
    • 再议 C++ 11 Lambda表达式
    • 基于Chrome插件的开发工具链
    • [ACM] HDU 1006 解题报告
    • Linux 编译安装 GCC 4.9
    • 又碰到了这个解谜游戏,顺带记下地址
    • 简单C++单元测试框架(支持一键切到GTest或Boost.Test)
    • 捣鼓一个协程库
  • 2013
    • std和boost的function与bind实现剖析
    • 不知道是哪一年的腾讯马拉松题目 照片评级 解题报告
    • Lua 挺好用的样子
    • VC和GCC成员函数指针实现的研究(三)
    • VC和GCC成员函数指针实现的研究(二)
    • VC和GCC内成员函数指针实现的研究(一)
    • 一个C++关于成员变量偏移地址的小Trick
    • ptmalloc,tcmalloc和jemalloc内存分配策略研究
    • POJ 2192 Zipper HDU 2059 龟兔赛跑
    • 从Javascript到Typescript到Node.js
    • 网络编程小结
    • 试试Boost.Asio
    • Lnmp yum 安装脚本 (for CentOS)
    • ARM 交叉编译环境搭建
    • Linux 编译安装 GCC 4.8
    • [记录]虚拟硬盘的压缩|磁盘写零
  • 2012
    • Boost.Spirit 初体验
    • “C++的90个坑”-阅读笔记
    • AC自动机
    • C++ 标准过渡期
    • 程序员修炼之道 -- 阅读笔记
    • [转载]狼与哈士奇
    • C++ 新特性学习(八) — 原子操作和多线程库[多工内存模型]
    • C++ 新特性学习(七) — 右值引用
    • 理解Protobuf的数据编码规则
    • 忆往昔ECUST的ACM时代
    • Linux编译安装GCC 4.7
    • JSON显示库 -- showJson (Javascript)
    • C++ 新特性学习(六) — 新的字符串编码和伪随机数
    • C++ 新特性学习(五) — 引用包装、元编程的类型属性和计算函数对象返回类型
    • C++ 新特性学习(四) — Bind和Function
  • 2011
    • C++ 新特性学习(三) — Regex库
    • C++ 新特性学习(二) -- Array、Tuple和Hash库
    • C++ 新特性学习(一) -- 概述+智能指针(smart_ptr)
    • Linux 和 Windows PowerShell 常用工具/命令 记录
    • 非常帅气的Linq to sql
    • 2011 Google Code Jam 小记
    • C++总是很神奇
    • 大学生创新项目[国家级]经费使用记录
    • 常用官方文档整理
    • 我们学校的IPV6很不错嘛
  • 2010
    • 线段树相关问题 (引用 PKU POJ题目) 整理
    • 2010 ACM 赛前笔记
    • POJ PKU 2596 Dice Stacking 解题报告
    • POJ PKU 3631 Cuckoo Hashing 解题报告
    • POJ PKU 1065 Wooden Sticks 3636 Nested Dolls 解题报告
    • HDU 3336 Count the string 解题报告
    • Hash模板 个人模板
    • ZOJ 3309 Search New Posts 解题报告
    • POJ PKU Let's Go to the Movies 解题报告
    • 注册表常用键值意义
    • PKU POJ 1724 ROADS 解题报告
    • 《神奇古今秘方集锦》&《民间秘术大全》
    • PKU POJ 1720 SQUARES 解题报告
    • POJ PKU 2155 Matrix 解题报告
    • PKU POJ 1141 Brackets Sequence 解题报告
    • PKU POJ 2728 Desert King 解题报告
    • PKU POJ 2976 Dropping tests 解题报告
    • PKU POJ 3757 Simple Distributed storage system 解题报告
    • GCD Determinant 解题报告
    • Southeastern European 2008 Sky Code 解题报告
    • HDU HDOJ 3400 Line belt 解题报告
    • 线性筛法求质数(素数)表 及其原理
    • HDU HDOJ 3398 String 解题报告
    • 树状数组模块(个人模板)
    • 浙江理工 省赛总结 team62 By OWenT of Coeus
    • POJ PKU 3659 Cell Phone Network 解题报告
    • USACO 2008 March Gold Cow Jogging 解题报告
    • C#格式化输出(记录)
    • 参加有道难题笔记
    • POJ PKU 2446 Chessboard 解题报告
    • POJ PKU 1986 Distance Queries 解题报告
    • 计算几何算法概览[转载]
    • 关于差分约束(转载)
    • POJ PKU 2826 An Easy Problem?! 解题报告
    • 数论模板(个人模板)
    • 简易四则运算(ACM个人模板)
    • Catalan 数
    • The 35th ACM/ICPC Asia Regional Tianjin Site —— Online Contest 1009 Convex 解题报告
    • JQuery扩展插件--提示信息
    • ACM 计算几何 个人模板
    • 解析网站字符串型参数 Javascript QueryString 操作 TQueryString类
    • POJ PKU 1474 Video Surveillance 解题报告
  • 2009
    • 模式匹配(kmp)个人模板
    • 并查集 模板
    • POJ 3267 The Cow Lexicon 解题报告
    • C/C++语言常用排序算法
    • POJ 2606 Rabbit hunt 2780 Linearity 1118 Lining Up 解题报告
    • 打造最快的Hash表(转) [以暴雪的游戏的Hash为例]
    • ECUST 09年 校赛个人赛第六,七场总结
    • ECUST 09年 校赛个人赛第三场部分解题报告(A,D,F,I)
    • 牛顿迭代解方程 ax^3+bX^2+cx+d=0
    • 09年8月9日 ECUST ACM 练习赛总结
    • 连接最多点直线 (OWenT 个人模板)
    • 点到直线距离 和 线段间最短距离 (OWenT 模板)
    • ECUST 09年 校赛个人训练赛第五场总结
    • ECUST 09年 校赛个人赛第八场(最后一场)总结
    • 09年8月14日 ECUST ACM 练习赛总结
    • 矩阵相关 (增强中)
    • Prime最小生成树(个人模板)
    • 最长单调子序列 复杂度nlog(n)
    • POJ PKU 2549 Sumsets 解题报告
    • POJ PKU 3277 City Horizon 解题报告
    • 我的ACM生涯
    • POJ PKU 2528 Mayor's posters 解题报告
    • POJ PKU 2378 Tree Cutting 解题报告
    • POJ PKU 1990 MooFest 解题报告
Powered by GitBook
On this page
  • 前言
  • 诡异的命名和符号
  • 设计核心
  • 默认不可变和可变借用唯一
  • 神一样的枚举类型和模式匹配机制
  • 返回值和语句块的最后一个值
  • 生命周期和转移语义
  • 泛型和操作符重载
  • 宏
  • 工程化
  • 测试框架
  • 单元测试、集成测试和压力测试
  • Mock API
  • 工具链管理(rustup.rs)
  • 构建系统和包管理
  • 写在最后

Was this helpful?

  1. 2018

初识Rust

Previous可执行文件压缩Next使用restructedtext编写xresloader文档

Last updated 5 years ago

Was this helpful?

前言

虽然我主要使用C++,但是最近也想学点现代化的新语言。初步想的是从和里先选一个。

这两年在国内很火,最大的特点莫过于语言层面提供了协程支持,能够极大地简化异步逻辑地理解。我之前也接触过一点,还写了个对比我的的性能。但是的语法我实在不喜欢,特别是那个不管啥类型声明都是反着来,感觉在复杂的类型下会非常反人类。而且听用过的人说的GC还很不稳定。另外之前有新闻说正在准备2.0,2.0版本即将加入泛型支持,然后导致很多语法不兼容和语法分析得重写。所以我还是懒得踩这个坑了,至少等2.0出来再说。

是Mozilla搞出来想拿来重写Firefox的。说实话Mozilla和Google还有点差距,导致的发展还比较慢。对比起来就是感觉很快就提供了一些快速可用的原型给大型项目使用,标准库也足够丰富。而还纠结在底层、语言层面的优化和最求极致。很多组件都还不成熟,编程设计模型也还没完全统一。

但是接触了一点以后,我发现真的是挠到了C++程序员的痒点,语言层面解决了用C++得费很多脑力和用各种奇技淫巧实现并且还不能完全阻止被绕过的质量控制问题,而且保留了C++很多编译期推断得高级特性。并且和C++一样,提供给你能力,但不限定你方法提供 零成本抽象(zero-cost abstractions) 或者说叫 零开销(zero-overhead)。

In general, C++ implementations obey the zero-overhead principle: What you don’t use, you don’t pay for. And further: What you do use, you couldn’t hand code any better.

Bjarne Stroustrup "Foundations of C++"

从整体来说,C++ 的实现遵循了零开销原则:你不需要的,无需为他们买单。更有甚者的是:你需要的时候,也不可能找到其他更好的代码了。

本贾尼·斯特劳斯特卢普 "Foundations of C++"

这是和很多其他语言不同的。比如你可以实现操作符重载,并且不会有可见性问题和冲突问题;今年的版本对宏的增强可以让你很容易实现很多语言支持的await功能,现在已经有不少Rust协程库了。感觉就是搞出来挖C++墙角的。

诡异的命名和符号

也有一些让初学者不太爽的地方。就是它为了解决一些工程上的问题提出了一系列概念,然后里面用的符号特别的奇葩。就算是已有的概念,它也非得搞个特立独行的命名。感觉完全就是增加学习成本。

比如后面会提到的生命周期,它非得用 '标识符 这种奇怪符号。别人都是用在字符(串)里的。还有宏,非得用感叹号结尾。

还有lambda表达式,我见过 (参数列表) => {代码块} 的,甚至不带括号的 参数列表 => 代码块 或者不带箭头的 (参数列表) {代码块} 的。偏偏使用竖线来声明参数 |参数列表| 代码块 。

还有其他语言的多分支处理几乎都是 switch ,的叫 match 。人家语言一般成功都叫success,的叫Ok。其他语言的interface,的叫trait。不过这个C++也叫trait而且它和C++的类似都有编译期推断所以还勉强说的过去。但是别人叫package 叫crate是什么鬼?

本来我觉得的函数声明的关键字是 func 已经很偷懒了。结果这货更短,是 fn 。之前看到一个文章是说啥来着现在完全没必要使用缩写的。因为现代的编辑器/IDE已经足够“聪明”了,并且也不缺那点磁盘空间。然后别的语言里的 abort 、 assert 之类, [Rust][1 用的 panic! 这种大量的缩写和奇怪的命名我感觉的增大了非常多学习成本。也不知道设计者怎么想的。

设计核心

默认不可变和可变借用唯一

神一样的枚举类型和模式匹配机制

// Send the message
match sender.send_message(&message) {
    Ok(()) => (),
    Err(e) => {
        println!("Send failed: {:?}", e);
        break;
    }
}

这里面其实是对sender.send_message(&message)的返回值做枚举判定,然后Ok的case绑定了一个空值,Err的case绑定了一个e,包含了错误信息。我认为这种方式比Exception机制要好得多得多得多。像C/C++之类得语言,会推荐返回错误码。但是这种错误码一般只有一个整数,能够提供得信息很少,而且如果被漏判了很难发现。而另一部分语言多使用异常机制,那么有个问题是你在使用的时候很容易忽略了他可能会抛个异常然后忘记了fallback。而这种机制兼顾解决了这两个问题。特别是不同的case绑定的数据类型可以不一样,这样为以后扩展和防止遗漏提供了最大的便利。比如我之前写的websoccket的小工具。

match message {
    OwnedMessage::Close(_) => {
        // Got a close message, so send a close message and return
        let _ = tx_1.send(OwnedMessage::Close(None));
        return;
    }
    OwnedMessage::Ping(data) => {
        match tx_1.send(OwnedMessage::Pong(data)) {
            // Send a pong in response
            Ok(()) => (),
            Err(e) => {
                println!("Send Pong failed: {:?}", e);
                return;
            }
        }
    }
    // print what we received
    OwnedMessage::Text(data) => {
        println!("Response: {:?}", json::stringify(data));
        let _ = tx_1.send(OwnedMessage::Close(None));
    }
    // print what we received
    _ => {
        let _ = tx_1.send(OwnedMessage::Close(None));
    }
}
if let Err(e) = sender.send_message(&message) {
    println!("Send failed: {:?}", e);
}

然后匹配还支持一些其他语言的语法糖,像这种:

let num = Some(4);

match num {
    Some(x) if x < 5 => println!("less than five: {}", x),
    Some(x) => println!("{}", x),
    None => (),
}

还有范围匹配和忽略范围匹配:

// 按范围匹配
let x = 'c';

match x {
    'a' ... 'j' => println!("early ASCII letter"),
    'k' ... 'z' => println!("late ASCII letter"),
    _ => println!("something else"),
}


// 忽略部分匹配项
let numbers = (2, 4, 8, 16, 32);

match numbers {
    (first, .., last) => {
        println!("Some numbers: {}, {}", first, last);
    },
}

还有不是很好懂的解引用语法糖:

struct Point {
    x: i32,
    y: i32,
}

fn main_1() {
    let p = Point { x: 0, y: 7 };

    let Point { x: a, y: b } = p;
    assert_eq!(0, a);
    assert_eq!(7, b);
}


fn main_2() {
    let p = Point { x: 0, y: 7 };

    match p {
        Point { x, y: 0 } => println!("On the x axis at {}", x),
        Point { x: 0, y } => println!("On the y axis at {}", y),
        Point { x, y } => println!("On neither axis: ({}, {})", x, y),
    }
}

这里面的解出的变量是放在Object的值的部分的,看起来十分诡异。然后这个解引用语法糖和上面的范围限制的语法糖结合,还有个新的语法糖:(用 @ 符号限定范围)

enum Message {
    Hello { id: i32 },
}

let msg = Message::Hello { id: 5 };

match msg {
    Message::Hello { id: id_variable @ 3...7 } => {
        println!("Found an id in range: {}", id_variable)
    },
    Message::Hello { id: 10...12 } => {
        println!("Found an id in another range")
    },
    Message::Hello { id } => {
        println!("Found some other id: {}", id)
    },
}

返回值和语句块的最后一个值

fn print_usage() -> i32 {
    123
}

这时候123是返回值,这和传统的语言不太一样。之所以说是语句块的“返回值”,是因为他还可以这么用:

let a = if abc {
    123
} else {
    456
}

甚至再嵌套一层:

let a = if abc {
    match def {
        Ok(m) => m,
        Err(f) => panic!(f.to_string()),
    };
} else {
    456
}

这就有意思多啦。怪不得不建议用 return 语句,因为这个的功能不是传统意义的 return 。

生命周期和转移语义

所谓意料之外的开销是指比如C++有很多隐式类型转换和复制构造。而且由于历史和优先保证正确性的原因,默认就是复制。像 std::string 传递的时候可能会产生的复制,以及一不小心模板推断出了个std::vector类型作为函数参数,那会导致整个复制。

#[derive(Clone)]
struct Point {
    x: i32,
    y: i32,
}

let p1 = Point{x: 1, y: 2};
let p2 = p1.clone();

泛型和操作符重载

比如重载 += 符号:

use std::ops::AddAssign;

struct Point {
    x: i32,
    y: i32,
}

impl AddAssign for Point {
    fn add_assign(&mut self, other: Point) {
        *self = Point {
            x: self.x + other.x,
            y: self.y + other.y,
        };
    }
}

let mut point = Point { x: 1, y: 0 };
point += Point { x: 2, y: 3 };

宏

另外它的宏还支持自定义过程宏,像这种的:

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

等价于

struct Point {
    x: i32,
    y: i32,
}

use std::fmt;

impl fmt::Debug for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Point ( x: {}, y: {} )", self.x, self.y)
    }
}

相当于魔改写代码了。官方文档还有个更蛇皮的例子:

extern crate proc_macro;
extern crate syn;   // 使用syn包来描述语法结构
#[macro_use]
extern crate quote; // 使用quote包来辅助扩展语法

use proc_macro::TokenStream;

// 为 #[derive(HelloWorld)] 提供语法支持。这样所有标记 #[derive(HelloWorld)] 的类型都会在编译期执行下面的代码
#[proc_macro_derive(HelloWorld)]
pub fn hello_world_derive(input: TokenStream) -> TokenStream {
    // Construct a string representation of the type definition
    let s = input.to_string();

    // Parse the string representation
    let ast = syn::parse_derive_input(&s).unwrap();

    // Build the impl
    let gen = impl_hello_world(&ast);

    // Return the generated impl
    gen.parse().unwrap()
}

fn impl_hello_world(ast: &syn::DeriveInput) -> quote::Tokens {
    let name = &ast.ident;
    // 给语法树注入hello_world()函数
    quote! {
        impl HelloWorld for #name {
            fn hello_world() {
                println!("Hello, World! My name is {}", stringify!(#name));
            }
        }
    }
}

然后,只要指定 #[derive(HelloWorld)] 就可以再编译期注入hello_world()函数:

extern crate hello_world;
#[macro_use]
extern crate hello_world_derive;

use hello_world::HelloWorld;

#[derive(HelloWorld)]
struct Pancakes;

fn main() {
    Pancakes::hello_world(); // 会输出 "Hello, World! My name is Pancakes"
}

工程化

测试框架

单元测试、集成测试和压力测试

在运行测试的时候,panic! 之类的宏不会再崩溃,而是输出错误信息。额外还有一些测试专用的宏 assert! 、 assert_eq! 、 assert_ne!等等。

额外还有一些函数标记:

  • #[should_panic] 表示这个函数一定会 panic 。而 #[should_panic(expected = "panic文本")] 还能指定 panic 的原因。

  • #[ignore] 用来标记默认情况下忽略这个测试。这时候用 cargo test -- --ignored 可以指定仅运行默认忽略的测试。

  • #[bench] 标记是一个压力测试函数,使用 cargo bench 来执行。压力测试的报告是纳秒级。

测试代码可以写在源代码里打标记(单元测试),也可以专门提出来写在 工程目录/tests 目录里(集成测试)。当写在 工程目录/tests 目录里的时候可以通过 cargo test --test 测试模块或文件名 来仅允许某个集成测试文件。

最后,cargo test --release 可以指定按release编译,然后运行测试。这样可以测试编译优化后的结果,也可以拿来做压力测试。

Mock API

其实这里的Mock API就是实现一个Mock的类型,然后 impl 原类型 for Mock类型 ,里面实现要mock的方法。在不能改变可变性(非mut)的参数里,使用 RefCell 来完成运行时借用检查。也就是所有权唯一性检查。RefCell 是运行时检查借用所有权的,万一破坏了这个规则会导致 panic,所以这时候单元测试就非常有用了,能提前发现大多数不正确的使用流程。

基本上这个测试框架已经覆盖了工程上的各种使用场景了。

当前版本已经提供了下面这些平台的支持。

  • aarch64-apple-ios

  • aarch64-linux-android

  • aarch64-unknown-cloudabi

  • aarch64-unknown-fuchsia

  • aarch64-unknown-linux-gnu

  • aarch64-unknown-linux-musl

  • arm-linux-androideabi

  • arm-unknown-linux-gnueabi

  • arm-unknown-linux-gnueabihf

  • arm-unknown-linux-musleabi

  • arm-unknown-linux-musleabihf

  • armv5te-unknown-linux-gnueabi

  • armv7-apple-ios

  • armv7-linux-androideabi

  • armv7-unknown-cloudabi-eabihf

  • armv7-unknown-linux-gnueabihf

  • armv7-unknown-linux-musleabihf

  • armv7s-apple-ios

  • asmjs-unknown-emscripten

  • i386-apple-ios

  • i586-pc-windows-msvc

  • i586-unknown-linux-gnu

  • i586-unknown-linux-musl

  • i686-apple-darwin

  • i686-linux-android

  • i686-pc-windows-gnu

  • i686-pc-windows-msvc

  • i686-unknown-cloudabi

  • i686-unknown-freebsd

  • i686-unknown-linux-gnu

  • i686-unknown-linux-musl

  • mips-unknown-linux-gnu

  • mips-unknown-linux-musl

  • mips64-unknown-linux-gnuabi64

  • mips64el-unknown-linux-gnuabi64

  • mipsel-unknown-linux-gnu

  • mipsel-unknown-linux-musl

  • powerpc-unknown-linux-gnu

  • powerpc64-unknown-linux-gnu

  • powerpc64le-unknown-linux-gnu

  • s390x-unknown-linux-gnu

  • sparc64-unknown-linux-gnu

  • sparcv9-sun-solaris

  • wasm32-unknown-emscripten

  • wasm32-unknown-unknown

  • x86_64-apple-darwin

  • x86_64-apple-ios

  • x86_64-linux-android

  • x86_64-pc-windows-gnu

  • x86_64-pc-windows-msvc

  • x86_64-rumprun-netbsd

  • x86_64-sun-solaris

  • x86_64-unknown-cloudabi

  • x86_64-unknown-freebsd

  • x86_64-unknown-fuchsia

  • x86_64-unknown-linux-gnu (default)

  • x86_64-unknown-linux-gnux32

  • x86_64-unknown-linux-musl (installed)

  • x86_64-unknown-netbsd

  • x86_64-unknown-redox

比如我这里本地安装交叉编译Linux-x86_64的工具链(主要为了发布的二进制不依赖发行版)并且使用nightly版本(主要有些新功能只能nightly支持)的安装流程就是:

rustup self update
rustup install nightly
rustup run nightly rustc --version
rustup default nightly
rustup update
rustup install stable-x86_64-unknown-linux-gnu
rustup install nightly-x86_64-unknown-linux-gnu
rustup target add --toolchain stable x86_64-unknown-linux-musl
rustup target add --toolchain nightly x86_64-unknown-linux-musl

Native库依赖和交叉编译

构建系统和包管理

年代比较久的语言(比如C/C++)里都没有统一的构建工具,所以各个不同的平台有很多的轮子。很多语言像python、java之类都有一些趋于统一的2-3个软件包管理器和构建工具,而像C/C++就极其混乱。GNU系用得多的是Makefile,近期还有个新的叫Ninja。macOS和Visual Studio系都有自己的工程文件格式,像Visual Studio还有不止一种(MSBUILD、NMAKE和基于VS工程文件的系统等等)。然后GNU系呢,因为Makefile的功能十分有限、和其他语言的集成也不方便,还晦涩难懂,所以又产生了一系列的生成Makefile的工具,像automake、autoconf、cmake等等。还有一些平台采用自己的构建系统,像gradle、yotta等等。

另外,包管理也完全不统一。Linux的发行版几乎都用自己的包管理工具,像Debian系用apt,Red Hat系用yum和的最近高出了个dnf,BSD系用ports,SUSE系用zypper。还有一些平台用pacman和brew之类的。微软又搞了个vcpkg。

对于依赖库的构建,我看到很多仓库里是写了个build.rs来指定构建流程的,文档里也只是说可以指定编译脚本,但是没有更多细节了,光看文档不知道这个build.rs有哪些功能要怎么写。而且它是怎么当脚本执行的?难不成是先编译build.rs然后执行完再构建依赖库或二进制的吗?现阶段只能看看别人怎么写的,然后照抄了。

  • 目录(TOC):

  • 开启脚注([^脚注名称]: 内容(支持多行))

  • 开启表格扩展(合并单元格、引用内的表格、列表内表格、单元格代码等)

  • [CommonMark spec][18]在基础Markdown上的扩展(当前引用的是0.27-0.28之间的一个commit),这个太多了我就不列举了

    • 啥都不填 => 自动检测代码

    • ignore => 忽略代码

    • test_harness => 不太清楚啥意思,好像是会添加到单元测试里去?

写在最后

前段时间我已经用rust简单地写了个命令行小工具给服务器用。接下来我会更深入研究下IO复用地部分和高并发编程地部分然后优化这个小工具,再来写第二阶段总结吧。

的设计核心感觉上就是在不牺牲效率的前提下,最大化地利用编译期分析,来帮助我们杜绝可能地错误。这点我是觉得用起来非常爽地地方。

比如说地数据类型默认是不可变的(当然很多函数式语言也这样)。像C/C++默认是可变的导致工程设计上很容易就忽略了提供一个const的函数。然后后来很难对一些场景做优化和假设。而默认不可变,可以使得编译期对很多变量、存储做优化。另外还有一个重要的设计是可变借用(mut)只能有一个。这样就可以准确地分析对象地生命周期然后控制对象什么时候析构。并且在多线程编程地时候,可以放心地认为这个对象不会在奇怪地地方被修改,也就不容易出现线程安全问题。

的枚举类型也很有意思。它允许你给枚举类型的每一项绑定一个不同的值。这就延伸出了对那种可成功可失败的API的推荐返回值是 Result<T, E> 。这是一个枚举值,其中有Ok(T)表示成功和成功的返回值,还有Err(E)表示失败和失败的返回值。当然如果有需要返回多种失败则可以换个有更多条目的枚举值。不同枚举值可以带出不同类型的信息。这也导致了我第一眼看这段代码的时候一直没理解这是什么奇怪的流程。

上面就是根据不同类型的消息执行不同的流程。我们看到有很多的 _ 是因为要求你处理所有的返回值。如果你要忽略,可以,显式告诉编译器。而 _ 就是告诉编译器我要忽略返回值或者case类型。这个要求对稳定性有很大的帮助,而我们以前的C++项目都是人工用编码规范的形式执行的。但是万一漏了或者有人偷懒,你完全发现不了,等爆发的时候已经GG了。

还有一个语法,是用来简化 match 的,但是我第一眼也没看懂。它主要是只处理成功或者只处理失败,忽略其他case的简写。拿上面发送消息举例就是:

和很多其他函数式语言一样,使用最后一个值作为语句块的“返回值”。如果语句块是一个函数,那么就是函数返回值。比如:

在看文档的过程中,看得出来的设计哲学是尽可能地无运行时开销并在编译期发现更多地错误。并且要求你关注失败和避免意料之外地开销。

另外在C++里,我们经常会用一些技巧去限制工程实现,以达到提高工程质量的效果,而且写技巧往往晦涩难懂。而没有这些历史包袱,就设计了一个比较良好的编译期检查。

生命周期分析会在编译期分析对象的所有权。它只允许一个对象被一个位置锁拥有,其他地方都只能 借用 。这有一个好处就是强制你去写安全的代码。而前面提到C++的一些隐式复制的问题,在里仅能隐式的转移(move语义),转移后生命周期也一并转移了,所有权仍然只有一个。这样就最大限度减少了不必要的复制。而真正的复制操作就是需要使用者显式去关心的。最简单的复制也需要使用一个 Clone 宏标记,然后调用clone方法。

的泛型没什么太多可说的。加类型限定的泛型大体上就是 <T: Display + Clone> 或 <T> where T: Display + Clone 这两种形式。有一点特别的地方就是因为有生命周期的概念,所以泛型里面可以带生命周期,并且类型限定也可以,比如: <T: Display + Clone + 'static> 这种就是泛型的类型匹配Display和Clone和静态生命周期(比如函数和静态变量都是静态生命周期的)。

的操作服重载相当于是要实现 里的 traits 。不过 std 库是可以禁用掉的,这种情况似乎操作符重载也GG了。

唯一有点特别的是仿函数重载,分成了 Fn, FnMut, 和 FnOnce 三种,对应的第一个 self 参数分别是 &self, &mut self 和 self。这个主要还是和的生命周期的编译期推断的设计模型有关。

有着极其难看懂但是究极变态得宏。

是可以禁用标准库的,然而像 format! 之类是标准库的内容,格式检查的traits也属于标准库。所以看上去感觉里面的内容检查之类不像是c++编译器那样的编译器hard code实现。我大致看了一下,的宏大致是 宏(参数) => 代码块 的形式。但是这个参数是可以直接 的。 而且由于后面那个跟的是代码块,所以可以更容易实现一些复杂的功能。

上面的用法非常的蛇皮,我没进入细看,感觉format!和println!之类可能也是使用了类似的技术。而且忍不住再吐槽一下的命名。 我看到 syn 的第一反应是同步机制(synchronization) ,谁知道他是语法(syntax)的缩写。

我看了下几个文档,还是 对宏的解析流程描述的比较完整。的宏还有一个挺有意思的地方是它受代码块的作用域的限制。现在看起来有一些复杂的概念,而且2018年说是要出2.0版,来支持await么?这个后面再细看吧。

自带单元测试、集成测试和文档测试框架并且可以直接通过执行 cargo test [查找名] 来运行。如果没有指定 查找名 ,这时候所有打了 #[cfg(test)] 标记的模块里,打了 #[test] 标记的函数会执行,还有文档里的测试代码也会执行。而如果指定了 查找名 ,所有测试名称中包含 查找名 的都会执行。

在 的 章节介绍了如何用 内部可变性 来实现对API的Mock,这对一些特别是依赖异步API数据的测试就很有用了。

工具链管理()

提供了一个官方的工具链管理器叫 。这个工具统一了我们管理的工具链、库、文档的流程。使用起来也很方便。目前功能和说明还比较简单,只在 上有,但是足够使用了。

初步接触下来只提供了编译器,链接器和汇编器还是用了gcc或者MSVC的。所以如果依赖C/C++的库的话,可以写编译脚本,然后直接引进来,还算方便。也可以直接用下载预编译好的各种平台的工具链,当然包含链接器。

这些是编译目标,然后工具链和库,也给了个的文档。基本上主要的平台都覆盖全了(除了很老的环境)。

上面也能看到,还提供了可以直接下载不依赖本地系统的工具链,这使得交叉编译方便了很多。比如可以用 x86_64-unknown-linux-musl 来编译不依赖发行版和系统库的Linux可执行程序,也可以 armv7-unknown-linux-musleabihf 编个什么程序扔路由器上跑。之前看到一些用写的工具,可以直接很容易地编译出嵌入式设备无依赖地二进制,就觉得很惊艳。现在也能做到了,我打算过段时间想个什么小工具扔家里路由器上玩玩。

这些都导致管理多平台和多目标的程序非常呃麻烦,费时费力。好在在这直接有一个规范化的系统。

构建系统 ()

首先是构建系统是。用于构建的项目。目前的文档还比较简单,详见 。基本上文档就是个快速上手版本,里面有些细节功能我没有写,我之前想找怎么附加链接选项就没查到。

直接集成了realse构建、文档构建和单元测试构建运行。也就是 cargo build --release 等于编译 [profile.release] 的内容。

有个有意思的功能是源替换(嗯,直译就这个: )。我打算等百度那个openssl的替代品()的跨平台适配好了,openssl的其他接口也适配全了,试试用它替换掉websocket依赖的openssl和openssl-dev。这种更换binding实现的机制是挺有意思,可以很方面我们做局部优化而不用背太多的技术债了。

包管理 ()

这个基本上就是类似 。提供了一个统一的官方的包管理。它们版本号的规则都基本一样,感觉都是抄的吧。不过总感觉这种先到先得的名字占用方式不是很好,感觉还是 用户名/包名 ,这种比较好。

文档管理 ()

对应包管理 () 系统,也附带了统一的文档管理系统 。基本上发布包的文档都可以在这里找到,结构还是挺简单易懂的。

自带了文档系统。它的 // 是普通注释, /// 是给函数或模块的文档注释, //! 是给文件的文档注释。文档采用Markdown格式。文档里没说,但是我看了下现在版本里的代码和注释,它使用了(提供基于的功能)在基础Markdown的基础上增加了:

对于代码块,新增了一批专用的语言标记

no_run => 标识代码能够编译但不能运行(猜测是不会在运行服务器上在线Run)

allow_fail => 标识代码能够编译和运行,但是允许运行失败

rust => 代码

compile_fail => 标识代码会编译失败

should_panic => 标识代码能够编译,但执行会 panic

最后还可以设置服务器来运行sample程序,主题现在似乎只有light/dark的选项。文档系统还支持通过增加metadata来增加功能,比如这个crate: 就通过Katex给文档系统增加了公式支持。

再记一下国内社区, 。

[18]:

golang
Rust
golang
goroutine压力测试
libcopp
golang
golang
golang
Rust
Rust
golang
Rust
Rust
Rust
Rust
Rust
Rust
Rust
Rust
Rust
Rust
golang
Rust
Rust
Rust
Rust
Rust
Rust
Rust
Rust
Rust
Rust
Rust
Rust
Rust
Rust
Rust
Rust
std::ops
Rust
Rust
Rust
Rust
匹配和控制AST
Rust
The Little Book of Rust Macros
Rust
Rust
《Rust 程序设计语言(第二版)》
RefCell 和内部可变性模式
rustup.rs
Rust
rustup.rs
Rust
https://github.com/rust-lang-nursery/rustup.rs
Rust
rustup.rs
平台支持状况
rustup.rs
go
Rust
Rust
cargo
cargo
Rust
cargo
https://doc.rust-lang.org/cargo/
cargo
cargo
Source Replacement
MesaLink
https://crates.io
vcpkg
NPM
https://crates.io
https://docs.rs
https://crates.io
Rust
https://docs.rs
Rust
pulldown-cmark
CommonMark
Rust
Rust
Rust
Rust
Rust
Rust
https://docs.rs/katex-doc/
Rust
https://rust.cc
http://spec.commonmark.org/