获得代码重用性和类型安保性的长处-Rust-在-编程中经常使用泛型 (获得代码重用的方法)
1.摘要
中的泛型可以让咱们为像函数签名或结构体这样的项创立定义,这样它们就可以用于多种不同的详细数据类型。上方的内容将触及泛型定义函数、结构体、枚举和方法,还将探讨泛型如何影响代码性能。
2.在函数定义中经常使用泛型
当经常使用泛型定义函数时,原本在函数签名中指定参数和前往值的类型的中央,会改用泛型来示意。驳回这种技术,使得代码顺应性更强,从而为函数的调用者提供更多的配置,同时也防止了代码的重复。
看上方的代码例子,定义了两个函数,配置都差不多,作用是区分寻觅slice中最大的i32和slice中最大的char,只是数据类型不同。
fnlargest_i32(list:&[i32])->&i32{letmutlargest=&list[0];foriteminlist{ifitem>largest{largest=item;}}largest}fnlargest_char(list:&[char])->&char{letmutlargest=&list[0];foriteminlist{ifitem>largest{largest=item;}}largest}fnmn(){letnumber_list=vec![34,50,25,100,65];letresult=largest_i32(&number_list);println!("Thelargestnumberis{}",result);letchar_list=vec!['y','m','a','q'];letresult=largest_char(&char_list);println!("Thelargestcharis{}",result);}
编译一下代码,输入如下:
咱们如今须要定义一个新函数,引进泛型参数来消弭这种因数据类型不同而造成的函数重复定义。为了参数化这个新函数中的这些类型,咱们须要为类型参数命名,情理和给函数的形参起名一样。任何标识符都可以作为类型参数的名字。这里决定T,由于传统过去说,Rust的类型参数名字都比拟短,理论仅为一个字母,同时,Rust类型名的命名规范是首字母大写驼峰式命名法(UpperCamelCase)。T作为type的缩写是大局部Rust程序员的首选。
假设要在函数体中经常使用参数,就必定在函数签名中申明它的名字,好让编译器知道这个名字指代的是什么。同理,当在函数签名中经常使用一个类型参数时,必定在经常使用它之前就申明它。为了定义泛型版本的largest函数,类型参数申明位于函数称号与参数列表两边的尖括号<>中,像这样:
fnlargest<T>(list:&[T])->&T
可以这样了解这个定义:函数largest有泛型类型T。它有个参数list,其类型是元素为T的slice。largest函数会前往一个与T相反类型的援用。
依照这个思维,咱们将代码变革如下:
fnlargest<T>(list:&[T])->&T{letmutlargest=&list[0];foriteminlist{ifitem>largest{largest=item;}}largest}fnmain(){letnumber_list=vec![34,50,25,100,65];letresult=largest(&number_list);println!("Thelargestnumberis{}",result);letchar_list=vec!['y','m','a','q'];letresult=largest(&char_list);println!("Thelargestcharis{}",result);}
一切似乎很顺利,尝试编译这段代码,编译器结果如下:
这次编译没有经过的要素Rust编译器用绿色标识进去了,缺少一个:std:cmp::PartialOrd,先暂且以为这个是Rust规范库要求的物品,加上从新编译一下试试:
fnlargest<T:std::cmp::PartialOrd>(list:&[T])->&T{letmutlargest=&list[0];foriteminlist{ifitem>largest{largest=item;}}largest}
从新编译结果如下:
咱们在代码中下了一个断点,能够口头到此处说明代码曾经没有疑问。实践上上方这个失误标明largest的函数体不能实用于T的一切或者的类型。由于在函数体须要比拟T类型的值,不过它只能用于咱们知道如何排序的类型。为了开启比拟配置,规范库中定义的std::cmp::PartialOrdtrait可以成功类型的比拟配置,咱们限度T只对成功了PartialOrd的类型有效后辈码就可以编译了,由于规范库为i32和char成功了PartialOrd。
3.在结构体中经常使用泛型
雷同也可以用<>语法来定义结构体,它蕴含一个或多个泛型参数类型字段。上方的代码片段定义了一个可以寄存任何类型的x和y坐标值的结构体Point:
structPoint<T>{x:T,y:T,}fnmain(){letinteger=Point{x:5,y:10};letfloat=Point{x:1.0,y:4.0};}
其语法相似于函数定义中经常使用泛型。首先,必定在结构体称号前面的尖括号中申明泛型参数的称号。接着在结构体定义中可以指定详细数据类型的位置经常使用泛型类型。
留意Point<T>的定义中只经常使用了一个泛型类型,这个定义标明结构体Point<T>关于一些类型T是泛型的,而且字段x和y都是相反类型的,无论它详细是何类型。
假设尝试创立一个有不同类型值的Point<T>的实例,看上方的代码:
structPoint<T>{x:T,y:T,}fnmain(){letwont_work=Point{x:5,y:4.0};}
在这个例子中,当把整型值5赋值给x时,就通知了编译器这个Point<T>实例中的泛型T全是整型。接着指定y为浮点值4.0,由于它y被定义为与x相反类型,所以将会获取一个像这样的类型不婚配失误:
假构想要定义一个x和y可以有不同类型且依然是泛型的Point结构体,咱们可以经常使用多个泛型类型参数。修正Point的定义为领有两个泛型类型T和U。其中字段x是T类型的,而字段y是U类型的:
structPoint<T,U>{x:T,y:U,}fnmain(){letboth_integer=Point{x:5,y:10};letboth_float=Point{x:1.0,y:4.0};letinteger_and_float=Point{x:5,y:4.0};}
如今一切这些Point实例都非法了!咱们可以在定义中经常使用恣意多的泛型类型参数,不过太多的话,代码将难以浏览和了解。当你发现代码中须要很多泛型时,这或者标明你的代码须要重构合成成更小的结构。
4.枚举中经常使用泛型
和结构体相似,枚举也可以在成员中寄存泛型数据类型。例如:
enumOption<T>{Some(T),None,}
Option<T>是一个领有泛型T的枚举,它有两个成员:Some,它寄存了一个类型T的值,和不存在任何值的None。经过Option<T>枚举可以表白有一个或者的值的形象概念,同时由于Option<T>是泛型的,无论这个或者的值是什么类型都可以经常使用这个形象。
枚举也可以领有多个泛型类型,例如:
enumResult<T,E>{Ok(T),Err(E),}
Result枚举有两个泛型类型,T和E。Result有两个成员:Ok,它寄存一个类型T的值,而Err则寄存一个类型E的值。这个定义使得Result枚举能很繁难的表白任何或者成功(前往T类型的值)也或者失败(前往E类型的值)的操作。
总结:当看法到代码中定义了多个结构体或枚举,它们不一样的中央只是其中的值的类型的时刻,无妨经过泛型类型来防止重复。
5.方法定义中的泛型
在为结构体和枚举成功方法时,一样也可以用泛型。看上方的代码:
structPoint<T>{x:T,y:T,}impl<T>Point<T>{fnx(&self)->&T{&self.x}}fnmain(){letp=Point{x:5,y:10};println!("p.x={}",p.x());}
这里在Point<T>上定义了一个叫做x的方法来前往字段x中数据的援用。留意必定在impl前面申明T,这样就可以在Point<T>上成功的方法中经常使用T了。经过在impl之后申明泛型T,Rust就知道Point的尖括号中的类型是泛型而不是详细类型。咱们可以为泛型参数决定一个与结构体定义中申明的泛型参数所不同的称号,不过依照惯例经常使用了相反的称号。impl中编写的方法申明了泛型类型可以定位为任何类型的实例,不论最终交流泛型类型的是何详细类型。
定义方法时也可以为泛型指定限度(constraint)。例如,可以决定为Point<f32>实例成功方法,而不是为泛型Point实例。代码如下:
implPoint<f32>{fndistance_from_origin(&self)->f32{(self.x.powi(2)+self.y.powi(2)).sqrt()}}
这段代码象征着Point<f32>类型会有一个方法distance_from_origin,而其余T不是f32类型的Point<T>实例则没有定义此方法。这个方法计算点实例与坐标(0.0,0.0)之间的距离,并经常使用了只能用于浮点型的数学运算符。
结构体定义中的泛型类型参数并不总是与结构体方法签名中经常使用的泛型是同一类型。看上方的代码:
structPoint<X1,Y1>{x:X1,y:Y1,}impl<X1,Y1>Point<X1,Y1>{fnmixup<X2,Y2>(self,other:Point<X2,Y2>)->Point<X1,Y2>{Point{x:self.x,y:other.y,}}}fnmain(){letp1=Point{x:5,y:10.4};letp2=Point{x:"Hello",y:'c'};letp3=p1.mixup(p2);println!("p3.x={},p3.y={}",p3.x,p3.y);}
在上方的代码中,Point结构体经常使用了泛型类型X1和Y1,为mixup方法签名经常使用了X2和Y2来使得示例愈加分明。这个方法用self的Point类型的x值(类型X1)和参数的Point类型的y值(类型Y2)来创立一个新Point类型的实例
在main函数中,定义了一个有i32类型的x(其值为5)和f64的y(其值为10.4)的Point。p2则是一个有着字符串slice类型的x(其值为"Hello")和char类型的y(其值为c)的Point。在p1上以p2作为参数调用mixup会前往一个p3,它会有一个i32类型的x,由于x来自p1,并领有一个char类型的y,由于y来自p2。println!会打印出p3.x=5,p3.y=c。
这个例子的目的是展现一些泛型经过impl申明而另一些经过方法定义申明的状况。这里泛型参数X1和Y1申明于impl之后,由于它们与结构体定义相对应。而泛型参数X2和Y2申明于fnmixup之后,由于它们只是相关于方法自身的。
6.泛型代码性能
不用担忧经常使用泛型会比使用详细类型的代码性能低。
Rust经过在编译时启动泛型代码的单态化(monomorphization)来保障效率。单态化是一个经过填充编译时经常使用的详细类型,将通用代码转换为特定代码的环节。
在这个环节中,编译器寻觅一切泛型代码被调用的位置并经常使用泛型代码针对详细类型生成代码。
上方看看这个怎样用于规范库中的Option枚举:
letinteger=Some(5);letfloat=Some(5.0);
当Rust编译这些代码的时刻,它会启动单态化。编译器会读取传递给Option<T>的值并发现有两种Option<T>:一个对应i32另一个对应f64。为此,它会将泛型定义Option<T>开展为两个针对i32和f64的定义,接着将泛型定义交流为这两个详细的定义。
编译器生成的单态化版本的代码看起来像这样(编译器会经常使用不同于如下假想的名字):
enumOption_i32{Some(i32),None,}enumOption_f64{Some(f64),None,}fnmain(){letinteger=Option_i32::Some(5);letfloat=Option_f64::Some(5.0);}
泛型Option<T>被编译器交流为了详细的定义。由于Rust会将每种状况下的泛型代码编译为详细类型,经常使用泛型没有运转时开支。当代码运转时,它的口头效率就跟如同手写每个详细定义的重复代码一样。这个单态化环节正是Rust泛型在运转时极端高效的要素。
rust为什么比c语言快
1. Rust简介Rust是一门由Mozilla研发的系统编程语言,于2010年首次推出,是一门开源、速度快、内存安全、并发性能出色的编程语言。 Rust的目标是成为一门适用于大规模软件系统的通用编程语言。 2. Rust比C语言快的原因与C语言相比,Rust有着更高的编译速度和更少的内存使用,这意味着Rust可以在提供更高的性能和更好的安全性的同时执行更少的指令。 此外,Rust对并发编程的支持也是其快速性的重要因素。 Rust的同时性模型是基于Actix的Actor模型,该模型对于web应用程序的构建非常有用。 使用Rust,我们可以创建多个Actor,将它们组合成一个应用程序,这样可以使应用程序更快、更可靠。 另一个让Rust比C快的因素是Rust中的编码和语法结构。 Rust的类型系统、生命周期、所有权和借用的复杂性始终保持了代码的清晰和易于理解,因此速度不会受到繁琐的代码或不必要的操作的影响。 3. Rust的优势Rust的另一个优势在于其内存安全特性。 Rust有一套完整的所有权和借用规则,这些规则确保了代码中不会出现内存泄漏、悬垂指针等错误。 此外,Rust是一种静态类型语言,这意味着我们可以在编译时检查代码是否有效,避免在运行时出现错误。 在Rust中,不仅能够检查是否符合要求,而且还能够检查代码是否有效。 Rust还可以生成高效的二进制文件,这意味着我们可以仅使用Rust编写整个应用程序并将其部署到服务器上,而无需使用其他语言或框架。 这简化了软件的开发、测试和部署过程。 4. Rust的使用场景由于Rust的速度和内存安全性,它非常适合开发网络应用程序、操作系统和游戏等大规模软件系统。 例如,游戏引擎使用C++,但是随着Rust的发展,越来越多的游戏引擎也开始采用Rust。 Rust还适用于网络安全工具、编译器和设备驱动程序等需要高性能和内存安全的项目。 与其他语言相比,Rust在这些方面具有很大优势。 5. 结论总结一下,Rust之所以比C语言快,是因为它采用了更高效的编码和语法结构、更好的并发性能和卓越的内存安全特性。 Rust还有很多其他的优点,这使其在开发大型、高效和内存安全的软件系统方面比其他语言更具优势。 因此,如果您正在寻找一种高速、安全和可靠的编程语言来开发您的下一个项目,那么Rust绝对是值得一试的。
编程时选用的程序设计语言,对软件的开发与维护的影响?
【CSDN 编者按】“如果我们把人类文明想象成汽车的话,那么软件开发行业就相当于汽车的引擎,编程语言就像引擎的燃料。”作为一名开发者,需跟随技术潮流的发展来学习新技术。2020年,你有计划新学一门编程语言吗?
本文作者从一名架构师的角度,详细分析了7种现代编程语言的优点与功能,你对哪门语言最感兴趣呢?
作者 | Md Kamaruzzaman,软件架构师
译者 | 弯月,责编 | 伍杏玲
封图| CSDN 下载于视觉中国
出品 | CSDN(ID:CSDNnews)
以下为译文:
如果我们把人类文明想象成汽车的话,那么软件开发行业就相当于汽车的引擎,而编程语言就像引擎的燃料。作为一名开发者,今年你应该学习哪种编程语言呢?
学习一种新的编程语言无疑是时间、精力和智力上的巨大投资, 但是学习一种新的编程语言可以提升你的软件开发技术力,促进你的职业发展。
在这里,我将献上一份现代编程语言的列表,这些语言不仅有助于提高你的生产力,而且还可以促进你的职业发展,并让你成长为更优秀的开发人员。这份列表还涵盖了非常广泛的领域:系统编程、应用程序开发、Web开发、科学计算等。
什么是现代编程语言?
“现代编程语言”这个说法本身就很含糊。许多人认为Python和JavaScript等语言是现代编程语言,还认为Java是一种古老的编程语言。实际上,这几种语言大约在同一时间出现:1995年。
大多数主流编程语言是上个世纪开发的:七十年代(如C)、八十年代(如C ++)、九十年代(如Java、Python、JavaScript)。这些语言在设计上并没有考虑现代软件开发生态系统:多核CPU、GPU、快速的互联网、移动设备、容器和云等。尽管许多语言中的许多功能都已进行一些改进,如并发等,而且在不断调整自己以适应时代,但它们依然保留了向后兼容性,无法抛弃那些过时的旧功能。
在这方面,Python就做得很好(某种意义上也未必是好事),Python 2和Python 3两者之间有明确的分界线。很多语言常常会为解决同一个问题提供十余种的方法,同时又没有顾及到开发人员的感受。根据StackOverflow的开发人员调查,大多数旧时的主流编程语言在“最可怕的语言”排名都名列前茅:
如果非要在新旧编程语言之间划个界限的话,那么应该是2007年6月29日,也就是第一台iPhone发行的时候。在这之后,编程语言界发生了很大变化。因此,在本文的列表中,我只考虑2007年以后的编程语言。
为什么要学习新语言?
首先,现代编程语言充分利用现代计算机硬件(多核CPU、GPU、TPU)、移动设备、大量数据、高速互联网、容器和云的优势。大多数现代编程语言会关注开发人员的体验,比如:
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。