旅游网站开发背景论文,网站建设与运营课程,网站设计图能用ps做么,手机开发者模式在哪里找本节已经涉及Rust学习曲线上的一个大坑#xff1a;泛型和特性了#xff0c;属于语言的深水区#xff0c;如果初学者#xff0c;建议看一眼知道有这个功能即可。
如果我们立足于功能实现#xff0c;那么做到像上一节那样就可以了#xff0c;从原理上来说#xff0c;每个…本节已经涉及Rust学习曲线上的一个大坑泛型和特性了属于语言的深水区如果初学者建议看一眼知道有这个功能即可。
如果我们立足于功能实现那么做到像上一节那样就可以了从原理上来说每个函数满足唯一的功能是一种好的设计软件工程里面“高内聚低耦合”是有利于系统的独立性的。
但是在调用的时候就需要有一点的心智了例如我们要读取点线面三种不同的shapefile就要记住三种不同的函数名和三种不同的绘图要素构建程序那也太麻烦了有没有一种方法让我们调用一种方法根据不同的参数类型自动适配不同的处理逻辑呢
如果你是学Python/Java/JavaScript这样的高级语言的同学你肯定已经有了自己的实现方法例如Python/Javascript作为动态类型的语言可以只写一个入口函数然后根据函数的输入参数自动去选择不同的分支来处理。
而Java也有同名方法覆盖利用不同的函数签名参数类型来解决用同名函数实现不同的逻辑流程。
而C则是可以用泛型和函数重载来实现这个功能。
那么在Rust里面如何解决呢
首先Rust不支持函数签名的类型推断。所有函数签名的类型必须写清楚这就杜绝了我们用动态类型的语言那样根据输入的参数来匹配。第二Rust也不直接支持函数重载为什么是不直接支持呢因为可以通过泛型和特性来实现函数重载也就是我们今天要做的内容。
老规矩先看结果 用同名的一个方法实现对于三种不同的shapefile读取并且获取三种不同的返回类型值那么这是这么实现的呢
先看代码
架构定义
首先定义了一个结构体这个空结构体的作用类似于Java里面的class。然后定义了一个特性trait特性这个东西在Rust中类似于Java里面的接口或者C里面的虚函数但是与接口和虚函数不同的时候特性可以直接在里面写实现也可以留空如果你是学Java的你把下面的代码理解为定义了一个接口并且定义了一个实现接口的工厂方法即可。在这里的特性中我们定义一个泛型T然后定义了一个方法就叫做read_shp输入产生就是一个shapefile的路径返回值是一个T ,这个T就是Rust中的泛型即代表一切可能的类型。 如果是没有接触过泛型的同学可能会问他既然代表一切类型那么你不是说了Rust是类型严格的语言么那系统编译器怎么知道这个T到底是哪种类型呢继续往下看
pub struct shp;
pub trait ReadShapfileT {fn read_shp(shp_path:str) - T;
}
然后我们开始写针对不同类型的shapefile的实现实现如下
//实现1读取polygon类型的shapefile通过返回值来匹配。
impl ReadShapfileVecPolygon for shp{fn read_shp(shp_path:str) - VecPolygon{let shp_read shapefile::read_as::_,shapefile::Polygon, shapefile::dbase::Record(shp_path).expect(format!(Could not open polygon-shapefile, error: {}, shp_path));let mut polygons:VecPolygon Vec::new();for (polygon, polygon_record) in shp_read {let geo_mpolygon: geo_types::MultiPolygonf64 polygon.into();for poly in geo_mpolygon.iter(){polygons.push(poly.to_owned());}}polygons}
}//实现2读取Polyline类型的shapefile通过返回值来匹配。
impl ReadShapfileVecLineString for shp{fn read_shp(shp_path:str) - VecLineString{let shp_read shapefile::read_as::_,shapefile::Polyline, shapefile::dbase::Record(shp_path).expect(format!(Could not open polyline-shapefile, error: {}, shp_path));let mut linestrings:VecLineString Vec::new();for (pline, pline_record) in shp_read {let geo_mline: geo_types::MultiLineStringf64 pline.into();for line in geo_mline.iter(){linestrings.push(line.to_owned());}}linestrings}
}
//实现2读取Point类型的shapefile通过返回值来匹配。
impl ReadShapfileVecPoint for shp{fn read_shp(shp_path:str) - VecPoint{let shp_read shapefile::read_as::_,shapefile::Point, shapefile::dbase::Record(shp_path).expect(format!(Could not open polyline-shapefile, error: {}, shp_path));let mut pnts:VecPoint Vec::new();for (pnt, pnt_record) in shp_read {let geo_pnt: geo_types::Pointf64 pnt.into();pnts.push(geo_pnt.to_owned());}pnts}
}
简要的解释一下 在Rust的语法中impl 特性名称返回值 for 结构名 这个语法代表了具体的实现而里面那个 返回值 部分就是我们定义的泛型T注意T只是一个代称习惯性用大写字母你用ABCDEFG或者abcdefg都是可以的。 然后在这个实现里面去写具体方法的read_shp的实现写完之后就可以通过在调用的时候再指定返回类型来适配具体调用哪个方法了。
看到这里可能有同学又有问题了
上一节直接写多个方法多实用啊读polygon就是polygon方法读point就是point方法干净整洁又高效干嘛搞这么麻烦 实际上两种方式都是可以的每个方法用独立名称进行调用实际上是一种比较传统但是很高效的方式C语言里面写操作系统内核都是这样写的。
而后面这种则是现代编程架构里面推荐的用同名函数来减少调用者的心智负担反正都是读取shapefile我干嘛还要记那么多个函数名啊为什么不能用一个函数就全部解决了呢 所以在软件开发中同名函数通过定义不同的返回值或者输入值来区分的方式叫做多态而在Rust里面多态又可以分为静态分发即静态多态模式或者单一态模式以及动态分发即动态多态模式。
这种所谓的静态和动态在Rust中是针对编译而言的静态即代码和方法在被编译的时候就已经决定了编译的结果你的输入、输出、处理流程等都在编译的时候被固定下来了。
这种也是函数式编程的核心思想函数式编程强调不可变性和纯函数这意味着函数的输出只取决于它的输入而不依赖于外部状态
而动态则相反在编译的时候系统并不知道要输入的参数和输出的结果具体是什么类型只有在运行的时候根据输入的数据情况才知道。
一般来说动态语言中的动就是这种通常解释性的语言都是动态的例如Python/Javascript等。
当然Rust也是支持动态编译的但是在使用动态编译的时候需要有一系列的手续这个我们以后再说。
从两种编译模式来看静态模式的开销小安全性高但是灵活性比较差反之动态模式灵活性好但是开销大安全性和稳定性较差。
具体在开发过程中用哪种模式就仁者见仁智者见智了。
最后我们来画一个UML图就很容易看出来这个架构的实现思路了