当前位置: 首页 > 后端技术 > Python

Rust代码风格小贴士

时间:2023-03-25 19:57:20 Python

大部分文章翻译自RustDesignPatters。为了快速和简洁,Constructors没有严格翻译。Rust没有ClassName(*args,**kw_args)这样的构造函数,一般约定使用staticnew方法创建一个新的“对象”.pubstructVec{buf:RawVec,len:usize,}implVec{//新建一个空的`Vec`//注意,这是一个静态方法,第一个参数不包含`self`或`Self`//例子中的new方法不带任何参数,必要时也可以定义初始化的参数pubfnnew()->Vec{Vec{buf:RawVec::new(),len:0,}}}如果某个结构体的初始化很复杂,可以考虑使用建造者模式和格式!连接字符串的时候可以使用push来处理字符串concatenatestrings,在可变字符串上用push_str方法,或者用+。但是在混合文字字符串和非文字字符串的情况下,使用format!更方便。fnsay_hello(name:&str)->String{//可以先创建一个可变的String,然后进行push操作,但是这个操作显然有点麻烦//letmutresult="Hello".to_string();//结果.push_str(名称);//结果.push('!');//结果格式!(“你好{}!”,名称)}格式!通常是连接字符的最简洁和可读的方式,但它可能不是最有效的方式,如果一个字符串已经被预先分配并且长度已经确定,那么推入它通常更有效。使用私有成员来实现可伸缩性。使用私有成员来确保可以在不破坏稳定性的情况下更改结构。在定性条件下实现扩展。moda{pubstructS{pubfoo:i32,bar:i32,}}fnfunc(s:a::S){//因为bar是私有成员,所以使用例如//让a::S{foo:_,bar:b}=s;//结构语法提示bar是一个私有成员,所以我们//使用`..`跳过这个私有成员leta::S{foo:_,..}=s;}添加一个成员到结构是通常向后兼容。但是这样会导致原来的模式匹配在解构struct的时候抛出不完全匹配成员的错误。可以使用private成员,并在模式匹配中使用..来跳过一些成员,即使加入了新的成员,原有的模式匹配也不会被破坏。使用这种方法的缺点是需要在结构体中添加不必要的成员,可以将成员的类型设置为()避免运行时消耗,在成员名前加上_避免编译器抛出警告不使用变量。将集合视为智能指针并使用Deref功能使数据集合成为智能指针,提供具有所有权和借用数据的视图。structVec{...}implDerefforVec{类型目标=[T];fnderef(&self)->&[T]{...}}Vec拥有一些T,而&[T]切片是一些T的借用。为Vec实现Deref允许隐式转换&Vec到&[T],还包括自动去索引关系。大多数你希望Vec实现的功能实际上都是针对切片实现的。最常见的示例之一是String和&str。有关更深入的讨论,请参阅析构函数中的原始终结。Rust没有finally语句块,即无论函数如何终止将要执行的语句块,Rust都提供了Drop特性来定义对象被销毁时需要运行的代码。结构富;implDropforFoo{fndrop(&mutself){println!("退出");}}fnfunc()->Result<(),()>{Ok(())//Err(())}fnuse_foo()->Result<(),()>{//`Drop`use_foo完成运行后将被调用//实现的析构函数方法,因此将打印“exit”//注意:即使func失败,//析构函数仍将被调用methodlet_exit=Foo;功能()?Ok(())}fnmain(){use_foo().unwrap();}值得注意的是,Rust的Drop不如finally值得信赖,因为Drop中的代码在某些情况下可能无法运行(例如panicthreads),甚至会导致其他问题,例如链表过多。参考Drop-docDrop-RustBookRAIIiterationoveranOptionOption可以被认为是一个包含0或1元素的容器,并且因为它实现了IntoIterator特性,在某些情况下,你可以像使用Vec一样使用它。fnmain(){letturing=Some("Turing");让mutlogicians=vec![“咖喱”,“Kleene”,“马尔可夫”];逻辑学家。扩展(图灵);//等价于ifletSome(turing_inner)=turing{logicians.push(turing_inner);}}如果你想链接一个选项到一个现有的迭代器,使用.chain():fnmain(){letturing=Some("Turing");letlogicians=vec!["Curry","Kleene","Markov"];对于逻辑学家中的逻辑学家。iter().chain(turing.iter()){println!("{}是逻辑学家",logician);}}如果你能保证Option的值总是Some,你可以使用std::iter::once:usestd::iter;//one是最孤独的numberletmutone=iter::once(1);assert_eq!(Some(1),one.next());//只有一个,这就是我们得到的全部assert_eq!(None,one.next());因为Option实现了IntoIterator,所以我们也可以使用for来遍历它,相当于使用ifletSome(_),通常更喜欢使用iflet。参考std::iter::onceIterator::filter_map快速过滤元素并将其映射到Option或Result的迭代器ref_sliceOption-DocDefault特性顾名思义,Default为某种类型或结构提供默认值,大多数基本类型实现Default,对于复合类型如Cow、Box或Arc,如果T实现了Default,它会自动实现对应复合类型的Default。对于结构体,如果结构体的每个成员都实现了Default,则可以使用#[derive(Default)]自动实现。Default和手动构造方法(通常是new)的区别是Default没有参数,只能有一个实现。手动构建方式更加灵活,可以有多个参数,不同的实现方式。#[derive(Default,Debug)]structFoo{data:i32,}fnmain(){print!("DefaultvalueofFoo:{:#?}",Foo::default());}传递变量给闭包闭包默认借用捕获环境中的变量,可以使用move强制转移变量所有权。此外,您还可以使用变量重新绑定和子作用域让代码更加清爽。推荐letnum1=Rc::new(1);letnum2=Rc::new(2);letnum3=Rc::new(3);letclosure={//`num1`所有权移动letnum2=num2.克隆();//`num2`被克隆letnum3=num3.as_ref();//`num3`是借来的着法||{*num1+*num2+*num3;}};而不是让num1=Rc::new(1);让num2=Rc::new(2);让num3=Rc::new(3);让num2_cloned=num2.clone();让num3_borrowed=num3.as_ref();让闭包=移动||{*num1+*num2_cloned+*num3_borrowed;};推荐的写法优点是克隆出来的数据在一个明显的子范围内,比较清晰,可以在使用中尽快完成析构,缺点是需要额外缩进。使用mem::replace假设以下枚举类型enumMyEnum{A{name:String,x:u8},B{name:String},}假设你想在x=0时将A转换为B,同时保持B完好无损的。最直接的想法是使用clone,但是我们也可以使用std::mem::replace来减少cloneusestd::mem;enumMyEnum{A{name:String,x:u8},B{name:String}}fna_to_b(e:&mutMyEnum){//我们在这里可变地借用了`e`。这阻止我们直接更改它//如在`*e=...`中,因为借用检查器不允许这样做。所以//对`e`的赋值必须在`iflet`子句之外。*e=ifletMyEnum::A{refmutname,x:0}=*e{//这取出我们的`name`并放入一个空字符串//(注意空字符串不分配).//然后,构造新的枚举变体(它将//分配给`*e`,因为它是`iflet`表达式的结果)。MyEnum::B{name:mem::replace(name,String::new())}//在所有其他情况下,我们立即返回,从而跳过赋值}else{return}}正在处理链接列表时,使用std::mem::replace可以减少很多繁琐的处理选项克隆代码。建议多读链表。更多介绍请参考原文。临时可变性在很多情况下,我们需要准备和处理一些数据,然后这些数据将不再被修改,所以我们希望将变量从可变变为不可变。这时可以在嵌套语句块中处理数据或者重新定义一个变量。使用嵌套语句块letdata={letmutdata=get_vec();数据排序();数据};使用变量重新绑定letmutdata=get_vec();data.sort();letdata=data;