手机做推广比较好的网站有哪些,先进的网站开发技术,wordpress响应式播放器,wordpress的页面布局全系列合集 [Rust开发]用可视化案例讲Rust编程1.用Rust画个百度地图 [Rust开发]用可视化案例讲Rust编程2. 编码的核心组成#xff1a;函数 [Rust开发]用可视化案例讲Rust编程3.函数分解与参数传递 [Rust开发]用可视化案例讲Rust编程4.用泛型和特性实现自适配shapefile的读取 […
全系列合集 [Rust开发]用可视化案例讲Rust编程1.用Rust画个百度地图 [Rust开发]用可视化案例讲Rust编程2. 编码的核心组成函数 [Rust开发]用可视化案例讲Rust编程3.函数分解与参数传递 [Rust开发]用可视化案例讲Rust编程4.用泛型和特性实现自适配shapefile的读取 [Rust开发]用可视化案例讲Rust编程5.用泛型和特性实现自适配绘制和颜色设置 上一节本来准备结束的后来很多同学问说我觉得处理颜色那个地方太麻烦了凭什么要写两次写一次不行么
这里涉及到了静态语言的一个核心概念即函数单态化。 单态化monomorphization即 Rust 编译器为每个调用生成一个单独的、无运行时开销的函数副本因此该函数副本的运行效率与不使用泛型的函数的运行效率是一致的。 这是Rust对于泛型这种高级语法的解决方案Rust的编译器选择了编译期对此泛型的所有可能性实现单态化这样可以选择最高效率最低开销的运行。 所以不管你写不写最终编译的时候都会编译成多个函数不过对于实现来说静态语言就只能静态实现而对于提供对外调用接口的情况自然是记忆开销越小越好正如我们前几节写的利用泛型返回读取shapefile以及用泛型处理点线面的方法。
泛型这种东西仁者见仁智者见智有人说泛型实际上是加大了系统的复杂性和冗繁度但是对于高层架构人员来说有泛型实在太方便了……所以就得到了一个比较主观的说法
—— 泛型就是给造轮子的人用的。
除了泛型要实现这种方式还可以用Rust的另外一个高级特性动态反射即在运行时在检测相关类型的信息dyn。 dyn关键字用于强调相关trait的方法是动态分配的。要以这种方式使用trait它必须是“对象安全”的。 与泛型参数或植入型特质不同编译器不知道被传递的具体类型。也就是说该类型已经被抹去。因此一个dyn Trait引用包含两个指针。一个指针指向数据例如一个结构的实例。另一个指针指向方法调用名称与函数指针的映射被称为虚拟方法表各vtable。 impl trait 和 dyn trait 在Rust分别被称为静态分发和动态分发即当代码涉及多态时需要某种机制决定实际调动类型。 看到这里可能有同学就会觉得 既然是高级特性看不懂的同学就暂时别去纠结了我们来看看下面这个简单的例子
use std::{any::Any, ops::Add};
#[derive(Debug)]
struct year{y:usize
}
#[derive(Debug,Clone)]
struct dog{name:String,age:usize,
}fn double(s: dyn Any){if let Some(v) s.downcast_ref::u32() {println!(u32 double {:?},*v * 2);}else if let Some(v) s.downcast_ref::f32() {println!(f32 double {:?},*v * 2.0);}else if let Some(v) s.downcast_ref::String() {let x v.clone();let x2 v.clone();println!(string double {:?},x.add(_).add(x2));}else if let Some(v) s.downcast_ref::year() {let y year{y:v.y 1};println!(year double {:?},y);}else if let Some(v) s.downcast_ref::dog() {let mut d dog{name:v.name.clone(), age:v.age};if d.age 12{d.age 0;}else{d.age d.age * 2;}println!(dog double {:?},d);}
}
这里定义了一个叫做double的方法没有静态指定他的输入参数而是用dyn这个关键字这个就代表了Rust会采用动态分发即运行的时候才去确定它到底是什么内型。
然后在方法里面我们可以针对不同的参数类型要进行匹配相应的处理流程。这些参数可以是系统内置的参数例如整型、浮点型也可以是自定义的结构。
例如我们定义的叫做year的结构体double的意思就是明年所以只需要加1就可以了。而定义的dog的参数默认狗的最大年纪就是24岁所以如果你输入的狗的age小于12岁则可以double而大于12直接清零……
测试如下 可以看见最后两个测试如果输入的狗子的年纪是8岁double出来就是16而输入的是15则直接清零了……
但是这种写法与传统的impl for 类型 实际上是一样的只是对外部而言调用的只是一个方法而已。
不过这种写法很多人都觉得会破坏静态语言的固定性不建议这样做所以大家做个了解即可。
从编译器角度来说函数单态化会把动态分发给编译成N个单态化的函数……所以这样写并不会减少最后release出来的结果
我们也可以通过enum来实现参考上一节颜色那个部分即可。
用dyn的方式你可以在参数里面传入任意类型的参数然后在运行的时候在控制走哪条逻辑线但是有没有一种可能可以控制输入参数的类型但是又可以根据类型进行逻辑选择的呢答案当然是有那就是官方推荐的impl trait 模式。
而且官方在1.26之后的版本里面推荐使用impl trait的方式来编写类型可控的泛型如下所示
trait my_type:std::fmt::DebugstaticAny{fn double(self);
}impl my_type for i32{fn double(self) {println!(i32 double {:?},self * self);}
}
impl my_type for f32{fn double(self) {println!(f32 double {:?},self * self);}
}
impl my_type for String{fn double(self) {println!(String double {}_{},self,self);}
}
impl my_type for dog{fn double(self) {let mut d2 self.clone();d2.age d2.age 1;println!(dog double {:?},d2);}
}
代码非常简单定义了一个trait然后里面有一个方法就是针对这个trait进行一个double处理。
之后针对i32、f32、String和dog四种类型进行了逻辑实现最后测试如下 //先写一个简单的测试性功能调用文件//因为我们在trait里面实现了Any类型所以有type_id这个方法能够获取对象类型唯一值fn show_my_type(s: impl my_type){
if s.type_id() TypeId::of::i32(){
println!(i32 {:?},s);
}
else if s.type_id() TypeId::of::f32(){
println!(f32 {:?},s);}
else if s.type_id() TypeId::of::String(){
println!(String {:?},s);
}
else if s.type_id() TypeId::of::dog(){
println!(dog {:?},s);
}
s.double();
}
测试效果如下 如果在调用的时候我们输入了没有定义的类型IDE工具就会提示 如果没有IDE的话编译器就会自动检测出来说你输入的参数类型是没有被实现过的不让使用了 而为什么可以这样做又涉及到Rust具备函数式编程的设计思想了……函数式编程里面函数是一等公民函数也是一种对象是可以定义和传递的所以这里也通常把这种trait叫做trait对象如果要论起写法来下面两种写法效果是完全一样的
trait Trait {}fn fooT: Trait(arg: T) {
}fn foo(arg: impl Trait) {
}
但是在技术上T: Trait 和 impl Trait 有着一个很重要的不同点。当用前者时可以使用turbo-fish语法在调用的时候指定T的类型如 foo::(1)。在 impl Trait 的情况下只要它在函数定义中使用了不管什么地方都不能再使用turbo-fish。 最后我来封装一下读取shapefile的方法和构造trace的方法让调用者不在关心具体的类型 直接读取shape类型并且转换为Geometry
pub fn shapeToGeometry(shp_path:str)- VecGeometry{let shps:VecShape shapefile::read_shapes(shp_path).expect(format!(Could not open shapefile, error: {}, shp_path));let mut geometrys:VecGeometry Vec::new();for s in shps{geometrys.push(Geometry::f64::try_from(s).unwrap())}geometrys
}
用Geometry来构造traceimpl BuildTrace for traceParamGeometry{fn build_trace(self) - VecBoxScatterMapboxf64,f64 {let mut traces: VecBoxScatterMapboxf64,f64 Vec::new();for (geom,color) in zip(self.geometrys.iter(),self.colors.iter()){let mut tr match geom {Geometry::Point(_){let p:Point_ geom.to_owned().try_into().unwrap();traceParam{geometrys:vec![p],colors:vec![color.to_owned()],size:self.size}.build_trace()},Geometry::MultiPoint(_){let p:MultiPoint_ geom.to_owned().try_into().unwrap();let pnts:VecPoint p.iter().map(|p|p.to_owned()).collect();let color (0..pnts.len()).map(|i|color.to_owned()).collect();traceParam{geometrys:pnts,colors:color,size:self.size}.build_trace()},Geometry::LineString(_){let p:LineString_ geom.to_owned().try_into().unwrap();traceParam{geometrys:vec![p],colors:vec![color.to_owned()],size:self.size}.build_trace()},Geometry::MultiLineString(_){let p:MultiLineString_ geom.to_owned().try_into().unwrap();let lines:VecLineString p.iter().map(|p|p.to_owned()).collect();let color (0..lines.len()).map(|i|color.to_owned()).collect();traceParam{geometrys:lines,colors:color,size:self.size}.build_trace()},Geometry::Polygon(_){let p:Polygon_ geom.to_owned().try_into().unwrap();traceParam{geometrys:vec![p],colors:vec![color.to_owned()],size:self.size}.build_trace()},Geometry::MultiPolygon(_){let p:MultiPolygon_ geom.to_owned().try_into().unwrap();let poly:VecPolygon p.iter().map(|p|p.to_owned()).collect();let color (0..poly.len()).map(|i|color.to_owned()).collect();traceParam{geometrys:poly,colors:color,size:self.size}.build_trace()},_ panic!(no geometry),};traces.append(mut tr);}traces}
}
然后在调用的时候就可以直接一击完成了#[test]
fn draw_db_style2(){let shp1 ./data/shp/北京行政区划.shp;let color1 inputColor::Rgba(Rgba::new(240,243,250,1.0));let shp2 ./data/shp/面状水系.shp;let color2 inputColor::Rgba(Rgba::new(108,213,250,1.0));let shp3 ./data/shp/植被.shp;let color3 inputColor::Rgba(Rgba::new(172,232,207,1.0));let shp4 ./data/shp/高速.shp;let color4 inputColor::Rgba(Rgba::new(255,182,118,1.0));let shp5 ./data/shp/快速路.shp;let color5 inputColor::Rgba(Rgba::new(255,216,107,1.0));let mut traces:VecBoxScatterMapboxf64,f64 Vec::new();for (shp_path,color) in zip(vec![shp1,shp2,shp3,shp4,shp5],vec![color1,color2,color3,color4,color5]) {let gs readShapefile::shapeToGeometry(shp_path);let colors:VecinputColor (0..gs.len()).map(|x|color.to_owned()).collect();let mut t traceParam{geometrys:gs,colors:colors,size:2}.build_trace();traces.append(mut t);}plot_draw_trace(traces,None);
}
绘制效果如下 放大之后效果如下 注意顺义出现了一个白色底是因为做数据的时候顺义因为首都机场出现了一个环形构造我们在绘制Polygon的时候内部环设置为了白色如果不想用这个颜色也可以直接设置为输入色就可以了如下所示 打完收工。 所有例子和代码在以下位置
https://gitee.com/godxia/blog
008.用可视化案例讲Rust编程
自取。