用可视化案例讲Rust编程
1. 怎么能学会Rust
如果要列举Rust的优势,恐怕写个十条八条是写不完的,而且不管写哪条优势,都有很多同学跳起来反驳,比如我们说Rust比C/C++内存安全,肯定有同学说C++ 20也支持内存安全,或者我写C++比斯特劳斯特卢普写得更好,写了十年没有发生过任何内存泄露……
但是,反过来,我要说Rust最大的问题,恐怕没人会反驳,包括我这种花了三次才入门,然后又做砸了两个项目才勉强算学会,能够进入工程级开发的新人老手……
那就是:难学!
Rust的学习曲线,号称是所有编程语言中最陡峭的,就连Rust基金会,都把降低学习曲线,当成Rust发展的当务之急:
原文地址:https://blog.rust-lang.org/inside-rust/2022/04/04/lang-roadmap-2024.html
(注意:不是2023才提,是每一年的roadmap,都特么要提一次,这玩意儿真的不好学……公认)
官方的说法:
many people report a sense of high "cognitive overhead" in using it, and "learning curve" remains the most common reason not to use Rust. The fact is that, even after you learn how the Rust borrow checker works, there remain a lot of "small details" that you have to get just right to get your Rust program to compile.
翻译过来就是:这玩意儿会有很高的认知开销,陡峭的 学习曲线 是大家不会(没法)使用Rust的最常见原因。
因为即使了解了Rust里面最麻烦最焦头烂额的特性:借用检查器(
所有权机制
)的工作原理后,仍有许多乱七八糟的小细节需要去正确处理,否则Rust编译器会好好的教你做人……
所以,很多公司,把Rust当成了一个测试题,但凡能够自学学会这个可(cao)爱(dan)的语言的,起码自驱能力必然是MAX……
就以智商中人水平的我来说,学习Rust一共学了三次……
第一次花了两个月,把书和代码敲了一遍,自我感觉好像学会了,然后……很快啊,我告诉你,真的很快啊……(就和学英语一样)我就忘记了……
第二次,是疫情居家办公,反正在家,所以我又用了一个来月,再次入门,而且这次还专门用Rust写了一系列的工程,例如GRPC和Python扩展包啥的,然后又自我感觉又行了……结果上班不超过两个月……好吧,又忘记了。
就有第三次入门,这次我都差点放弃,心想爱咋咋地吧,反正遇上了,就照着网上的代码ctrl + CV 吧。。。就这样,我这个半桶水还用Rust基于PGRX写了好几个扩展插件,还应付了包括国密测评在内的不少工作……
要不说,写代码,赶dead line,才是学习唯一的光……
要说我这种中等智商 + 马上45学习能力大退化的(四舍五入知天命)的半大老头,都最后勉勉强强算学会了,我觉得其他同学学会应该也是没啥问题的。
所以今天我秉持一贯说人话的原则,来和大家聊聊怎么样更快的学会Rust(以及你想学的其他编程语言也行)
2. 战略上要藐视敌人
所有同学(哪怕类似我这种写了十几年代码的老码农),一眼抗拒的原因,很大可能就是看到了一些大神写的Rust典型编码风格的代码,例如:
- 引用与解引用的hello world的例子:
let reference = &4;
match reference {
&val => println!("Got a value via destructuring: {:?}", val),
}
match *reference {
val => println!("Got a value via dereferencing: {:?}", val),
}
let _not_a_reference = 3;
let ref _is_a_reference = 3;
let value = 5;
let mut mut_value = 6;
match value {
ref r => println!("Got a reference to a value: {:?}", r),
}
match mut_value {
ref mut m => {
*m += 10;
println!("We added 10. `mut_value`: {:?}", m);
},
}
- 泛型的hello world:
impl <A: TraitB + TraitC, D: TraitE + TraitF> MyTrait<A, D> for YourType {}
impl <A, D> MyTrait<A, D> for YourType where
A: TraitB + TraitC,
D: TraitE + TraitF {}
- 生命周期的hello world
#[derive(Debug)]
struct Borrowed<'a> {
x: &'a i32,
}
impl<'a> Default for Borrowed<'a> {
fn default() -> Self {
Self {
x: &10,
}
}
}
就这种写法,就算你是一个资深C/C++开发工程师,都会发懵,更别说我这种写Python的二流码农了……
第一时间被劝退了有没有。
所以,开始学习的时候,千万别去大神们写的标准Rust编码风格、模式优美,代码简练的所谓的优质开源源码,你得从简单的看起,这样才能真正明白是干嘛的。
为什么说看简单的能入门呢?我们下面看看两个大家都认为很难,但是实际上看一眼就发现很简单的东西,就是GDAL的源码:
//带弧线的几何面积计算
double OGRCircularString::get_AreaOfCurveSegments() const
{
double dfArea = 0.0;
for (int i = 0; i < getNumPoints() - 2; i += 2)
{
const double x0 = getX(i);
const double y0 = getY(i);
const double x1 = getX(i + 1);
const double y1 = getY(i + 1);
const double x2 = getX(i + 2);
const double y2 = getY(i + 2);
double R = 0.0;
double cx = 0.0;
double cy = 0.0;
double alpha0 = 0.0;
double alpha1 = 0.0;
double alpha2 = 0.0;
if (OGRGeometryFactory::GetCurveParameters(
x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2))
{
const double delta_alpha01 = alpha1 - alpha0;
const double delta_alpha12 = alpha2 - alpha1;
dfArea += 0.5 * R * R *
fabs(delta_alpha01 - sin(delta_alpha01) + delta_alpha12 -
sin(delta_alpha12));
}
}
return dfArea;
}
——咦,不是说GDAL/CPP很难么?(恐怕绝大部分做GIS的同学,都没有看过GDAL的源码)为什么这个方法我能看懂?
现在回过头来,软件的组成方式是什么?软件最底层的啥?有同学说,当然是指令
啊——CPU
的指令集
是软件最底层的组织方式,所以吧啦吧啦……
好吧,不谈底层,那是硬件码农们的领域,对于我们这些软件码农来说,我们能够接触到的最底层的代码组织模式就是——语句,每个程序,都是由执行具体操作的语句所组成的,然后这些语句,集合起来,形成的函数(或者叫方法也行,function
),而一个函数则是一个一个最基本的功能逻辑实现体。
所以,我们在学习语言的时候,先不要一上来就高屋建瓴的去玩高端局,咱们得从最简单的入手,那么写起来也就简单了。
所以这次我们通过最简单的一个学习流程,来和大家一同来学习Rust的开发,别担心,你可以没有任何Rust的开发经验,但是你得有基础的代码编写能力,不管是Java、Python、JavaScript还是C#也都行。
恩,这里写的东西,主要是给我们GIS圈的同学看的,所以一些GIS基本概念,例如坐标
、几何要素
、点线面
、mapbox
、shapefile
、geojson
啥的,我就不解释了……
老规矩,先看效果,在讲实现——用Rust绘制一个百度风格的地图:
(啥,你问我为什么老是绘制百度地图,不能绘制点其他的风格么?答……百度风格挺好看的,而且最简单……)
放大之后:
看完之后,可能有同学会问:虾神,你这个地图怎么鸡里鸡气的(JavaScript的鸡)……
好吧,Rust本身就不是用来做可视化的,他的可视化能力不能说没有吧,起码和没有也差不多了……这个可视化用的是Rust封装了plotly,然后在web上绘制出来的,就是下面这个:
用Rust封装之后,就是这个:
所以,就不要在那些细节了,再说:
至于怎么实现的,我们下回分解。