范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Rust所有权探索

  变量在函数调用过程中的传递fn main() {     let data = vec![5, 7, 3, 1];     let v = 7;      match find_pos(data, v) {         Some(pos) => println!("Found {} at {}", v, pos),         None => println!("{} Not found", v),     } }  fn find_pos(data: Vec, v: u32) -> Option {     for (pos, item) in data.iter().enumerate() {         if *item == v {             return Some(pos);         }     }      None }
  在上面的示例代码中,main()函数定义了一个动态数组data和一个值v,然后将其传递给函数find_pos,在data中查找v是否存在,如果存在则返回v在data中的下标,如果不存在则返回None。
  data 作为动态数组,因为大小在编译期无法确定,所以放在堆上,并且在栈上有一个包含了长度和容量的胖指针指向堆上的内存。
  在调用find_pos函数时,main()函数中的局部变量data和v 作为参数传递给了find_pos(),因此,它们会被放在 find_pos()调用栈中的参数区:
  参数引用
  像Java等大多数编程语言的做饭,现在堆上的内存就有了两个引用,并且每次把data作为参数传递一次,堆上的内存就会多一次引用。
  那么造成的问题就是,无法得知这些引用到底能做什么操作,堆上内存什么时候能够释放,也无法确认。如果有多个调用栈引用堆上的内存时,给内存管理带来很大挑战。同时,引用是可以隐式产生的,随意性太大,例如Java中随处可见的按引用传参,它们可读可写,有很大的权限。
  对于这种堆内存多次引用的问题,传统语言有如下解决方案:C/C++要求开发者手动处理Java等语言使用追踪式GCObjective-C/Swift使用自动引用计数(ARC)
  以上方案,都各有利弊,都是从管理引用的角度来考虑的。
  但是,此问题本质上是因为堆上的内存会被随意引用。所有权和Move语义
  Rust对于值的使用,给出了以下规则:一个值只能被一个变量所拥有,这个变量称为所有者。一个值同一时刻只能有一个所有者。也就是说,不能有两个变量拥有相同的值。所以在变量赋值、参数传递,函数返回等行为,旧的所有者会把值的所有权转移给(Move)新的所有者,保证单一所有者约束。当所有者离开作用域,其拥有的值被丢弃,内存得到释放。一旦所有权转移,之前的变量就不能访问。
  在这三个所有权规则的约束下,上面示例的引用问题可以这样解决:
  所有权和move
  原先main()函数中的data,被移动到find_pos()后,就失效了,Rust编译器会保证main()函数随后的代码无法访问这个变量,就确保了堆上的内存依旧只有唯一的引用。解决了堆上数据的多重引用。
  main()函数中在把data传递给find_pos()后,还想让main()函数能够访问的化,可以调用clone()方法,把data复制一份出来。
  但是这种手动复制,会让代码变得复杂,一些只存储在栈上的简单类型数据,如果要避免所有权转移之后不能访问的情况,只能频繁的手动clone复制。
  Rust给出了两种方案:如果不希望值的所有权被转移,可以让数据结构实现Copy trait,这样在赋值或传参的时候,值会自动按位拷贝(浅拷贝)。Rust在设计的时候,已经保证了无法为一个在堆上分配内存的结构实现Copy。例如String等结构时不能实现Copy的。在这种情况下,如果不希望值的所有权被转移,又无法使用Copy语义,那么可以使用"借用"数据。Copy语义和Copy trait
  符合Copy语义的类型,在赋值或传参时,值会自动按位拷贝。
  如果数据类型没有实现Copy trait,在赋值或者函数调用的时候无法Copy,那么就按默认的Move语义。
  也就是说,要移动一个值,如果值的类型实现了Copy trait,就会自动使用Copy语义进行拷贝,否则使用Move语义进行移动。
  但是Copy trait也有一定的限制,Copy trait和Drop trait 不能共存。一旦你实现了Copy trait,就无法实现Drop trait。反之亦然。实现了Copy trait 的数据结构fn is_copy() {}  fn types_impl_copy_trait() {     is_copy::();     is_copy::();      // 所有的整数类型都是 copy     is_copy::();     is_copy::();     is_copy::();     is_copy::();     is_copy::();     is_copy::();     is_copy::();     is_copy::();     is_copy::();      is_copy::();     is_copy::();      // 函数指针是copy     is_copy::();      // 裸指针是 copy     is_copy::<*const String>();     is_copy::<*mut String>();      // 不可变引用是 copy     is_copy::<&String>();     is_copy::<&[Vec]>();      //对于数组/元组,如果其内部类型是 copy,那么它们就是 copy     is_copy::<[u8; 4]>();     is_copy::<(&str, &str)>(); } fn types_not_impl_copy_trait() {     // DST 类型不是 copy     // is_copy::();     // is_copy::<[u8]>();      // 有堆内存的类型不是 copy     //is_copy::>();     //is_copy::();      //可变引用不是 copy     //is_copy::<&mut String>();      // 对于数组/元组,如果其内部类型不是 copy,那么它们也不是copy     //is_copy::<[Vec;4]>;     //is_copy::<(String,u23)>(); }原生类型(函数、不可变引用、裸指针)实现了Copy。数组和元组,如果其内部的数据结构实现了Copy,那么它们也实现了Copy。可变引用没有实现Copy。非固定大小的数据结构,没有实现Copy。Borrow语义
  Borrow语义通过引用语法(&或者&mut)来实现。
  在Rust中,"借用"和"引用"是一个概念。所有的引用都只是借用了"临时使用权",它并不破坏值的单一所有权约束。
  在默认的情况下,Rust的借用都是只读的。只读借用
  本质上,引用是一个受控的指针,指向某个特定的类型。
  其他传统语言,函数传参有两种方式:传值(pass-by-value)和 传引用(pass-by-reference)。
  比如在Java,给函数传一个整数,这是传值,与Rust中的Copy语义一致。
  给函数传一个对象,或者是任何堆上的数据结构,Java会自动隐式地传引用。
  但是Java的引用是对象的别名,这也导致随着程序的运行,同一块内存的引用到处都是,不得不依赖GC进行内存回收。
  Rust中没有传引用的概念,Rust所有的参数传递都是传值,不管是Copy还是Move。
  所以,在Rust中,必须显式地把某个数据的引用,传给另一个函数。
  Rust的引用实现了Copy trait,所以按照Copy语义,此引用会被复制一份交给要调用的函数。
  对这个函数来说,它并不拥有数据本身,数据只是临时借给它使用。所有权还在原来的拥有者那里。
  在Rust里,引用是一等公民,和其他数据类型地位相等。fn main() {     let data = vec![1, 2, 3, 4]; // data 的生命周期比sum()中对data的引用要长     let data1 = &data; // 借用不能超过值的生存期                        // 值的地址是什么?引用的地址又是什么?     println!(         "address of value: {:p}({:p}),address of data: {:p},data1: {:p}", //0x8aa19bf8a8(0x8aa19bf8a8),address of data: 0x8aa19bf948,data1: 0x8aa19bf8c0         &data,                                                            //0x8aa19bf8a8         data1,                                                            //0x8aa19bf8a8         &&data,                                                           //0x8aa19bf948         &data1                                                            //0x8aa19bf8c0     );      println!("sum of data1: {}", sum(&data)); // sum 函数处在 main() 函数下一层调用栈中,它结束后main函数还会继续执行      // 堆上数据的地址是什么     println!(         "address of items:[{:p},{:p},{:p},{:p}]", //address of items:[0x1cc59705670,0x1cc59705674,0x1cc59705678,0x1cc5970567c]         &data[0],                                 //0x1cc59705670         &data[1],                                 //0x1cc59705674         &data[2],                                 //0x1cc59705678         &data[3],                                 //0x1cc5970567c     ); }  fn sum(data: &[u32]) -> u32 {     //值的地址会改变吗,引用的地址会改变吗     println!(         "address of value: {:p},addr of ref: {:p}", //address of value: 0x1cc59705670,addr of ref: 0x8aa19bf6d0         data,                                       //0x1cc59705670         &data                                       //0x8aa19bf6d0     );     data.iter().sum() }  // 只读引用 实现了 Copy trait,也就意味着引用的赋值、传参都会产生新的浅拷贝 // 虽然data 有很多只读引用指向它,但是堆上的数据依旧只有data 一个所有者:值的任意多个引用并不影响所有权的唯一性。
  借用
  data1、&data和传到sum()里的data1" 都是指向data本身,这个值的地址是固定的。但是它们引用的地址都是不同的。这是因为 只读引用实现了Copy trait,也就是意味着引用的赋值、传参都会产生新的浅拷贝。堆上数据依旧只有data一个所有者。值的任意多个引用并不影响所有权的唯一性。借用的生命周期
  借用(引用)不能超过(overlive)值的生存期。
  堆变量的生命周期不具备任意长短的灵活性,因为堆上内存的生死存亡,跟栈上的所有者牢牢绑定。而栈上内存的生命周期,又跟栈的生命周期相关,所以,我们只需要关心调用栈的生命周期。可变借用/引用
  为了保证内存安全,Rust对可变引用的使用做了严格的约束:在一个作用域内,仅允许一个活跃(真正被用来修改数据的)的可变引用。在一个作用域内,活跃的可变引用(写)和只读引用(读)是互斥的,不能同时存在。就像并发下数据的读写互斥。一个值可以有多个只读引用。引用的生命周期不能超出值的生命周期。

2年4支球队,控卫神话消失!隆多被迫退役?球迷自作自受两年前控卫神话2年之前,拉简隆多简直成为了人们眼中的控卫神话,他在季后赛的舞台上,用一次又一次惊为天人的传球,撕开了对手的防线,帮助洛杉矶湖人夺得了总冠军奖杯。可能隆多自己也没有想坠落英国的陨石中发现地外水,或能揭开地球水来源之谜?科幻网9月20日讯(秦莹莹)水是生命之源,地球上所有生物都逃不过。在太阳系中,只有我们的地球看上去像个地水球,因为只有地球的表面有液态水存在,而且海洋的面积占到了地球表面积的71。佛山满满创新与活力的湾区故事,每日在这里上演繁华都市散发着人间烟火味,风光如画的乡村绿树繁花连片成林,车间厂房机器轰鸣,实验室里创造奇迹坐落于珠三角腹地的佛山,是粤港澳大湾区重要节点城市,如今,面对粤港澳大湾区建设国家战略机85分钟绝杀,21逆转!阿森纳踢出18年最佳开局,力压曼城登顶北京时间8月28日0030,英超第4轮,米特洛维奇先下一城,厄德高扳平,加布里埃尔绝杀,最终阿森纳主场21逆转富勒姆,取得开局4连胜。这是阿森纳队史第三次取得英超开局4连胜,他们前10,2连胜!曼联改写19年纪录,第14飙升第6,C罗开局4轮0球0助北京时间8月27日晚,英超第4轮率先迎来一场比赛,曼联客场依靠B费的唯一进球,以10击败南安普顿,取得2连胜,积6分升至积分榜第6位。作为英超13冠王,曼联前2轮分别输给12布莱顿体坛三镇外援斯坦丘仍未伤愈或缺席23日罗马尼亚对阵芬兰直播吧9月23日讯据体坛周报报道,三镇外援斯坦丘目前依旧没有从伤病中康复,他有可能将会无法在罗马尼亚23日对阵芬兰的比赛中登场。斯坦丘进入罗马尼亚队训练营以后,一直是在保守训练。球财大气粗?詹姆斯师弟或成为辽宁队的第三外援?根据一些小道消息,CBA卫冕冠军辽宁队有意签下目前处于自由球员状态的前NBA球员也是CBA老熟人加卡尔桑普森(JaKarrSampson)作为球队新赛季的大外援。桑普森出生于199宏远再起飞大小外援就位,三少已留队,锋线建队核心已建立朱芳雨小官宣,签了2米08的大外援,没在国内打球过,目前等待来华签证!根据前面网传的前NBA蒙塔埃利斯的堂弟,奥克塔维尤斯埃利斯和百科上的2。08米身高基本吻合,坐实了大外援的人选夏日小结夏日小结6月21日夏至后至今天9月23日全是今年的夏天。安逸的夏天如何过才算是对得起夏天呢?你夜里睡在屋外数过天上的星星,数了星星后或思索过宇宙的为人,或者是想起了你儿时的夏日也是故乡的夏日故乡的季节里,我最喜欢夏日。因为它有带着露水的清晨,有燃烧着像火一样的黄昏,有凉风习习满天繁星的夜晚。而且它是那样的丰富,一天里发生的故事足够让你对它记忆一生。上学的时候,只要下课挚友语音版夏日情结以挚为心,携友同行。大家好,这里是挚友语音版!今天,要与大家分享的是刊登在挚友杂志上面的一篇散文夏日情结,作者张娜。五月的江南,空气中总是弥漫着一阵阵栀子花的清香,除此之外,还带有
自比管仲乐毅,诸葛亮的人生悲剧,是因其一开始的目标就定错了?东汉末年,宦官当道,吏治腐败,军阀割据,战乱连年,民不聊生。天才的诸葛亮生于这乱世,这是他的悲哀。他哀叹自己是苟全性命于乱世,不求闻达于诸侯。但乱世出英雄,又正是乱世,给了他施展才人生不要给自己设限,很多障碍原本不存在,只是自己的胆怯在作祟契诃夫有句名言有教养不是吃饭不洒汤,而是别人洒汤的时候别去看他。在这个社会当中,情商高的人往往能够越来越讨喜,也能够跟别人拥有更长久的关系。你也会发现情商很高的人往往有着很出色的情着太阳的温暖忙碌人生的希望当冬的阳光穿过树枝,透过发黄的树叶,照耀着干净的大地时,取暖的小生灵们静静待在有阳光的地方。一块石缝下几棵小草簇拥着,心尖已点露鹅黄,像一滴露珠轻轻滑过叶片。时光犹如发黄的老照片,太平天国分封的六大首义王,文化水平多高了?1秀才天王洪秀全(1814年1864年)今广东省广州市花都区人,客家人。出生于一个耕读世家,7岁在村小读书13岁参加县试名列前茅,但参加府试时落第次年辍学边劳动边在家自学15岁做私痛!太痛了!图文盘点中世纪酷刑,胆小勿看!每一道酷刑都是一种延续生命痛苦的艺术,它把人的生命分割成上千次的死亡,在生命停止之前制造最精细的痛苦。今天给大家分享中世纪时期真实的酷刑记载,友情提示,脑补能力强的胆小的朋友们谨慎一代宠臣和珅为何会惨死狱中?乾隆皇帝晚年,册立了皇十五子永琰为太子。就在密昭宣布的前一天晚上,和珅带着几大箱子奇珍异宝叩开了十五阿哥嘉亲王府的大门,呈上了一早就精心准备好的玉如意,来暗示自己的拥戴之意。然而就慈禧连喝了十口厨子送的汤,赞不绝口,但是最后为何却把厨子杀了?清末宫中有一位厨子,为慈禧做了一碗汤,慈禧尝过之后赞不绝口,就连续的喝了十口,看起来这位厨子就要加官进爵,升官发财了,但是事情并非这么简单,这个厨子最后竟然被慈禧给杀头了,那么有的令马云甘拜下风的人是什么神人?中国电子数据产业集团正式成立被马云称为天才前辈的人到底是什么神人?是任正非蔡崇信,不不不,这些人在马云眼里也只是一个普通人,但有这么一个人让马云见了都害怕。马云说,如果他还在,自己小米裁员!特斯拉裁员!Zara中国换帅丨一周财经盘点亿邦原创小米裁员!特斯拉裁员!Zara中国换帅!一年亏4亿!充电怪兽连亏五季!大消费回暖!贝泰妮股价涨超7,好想你涨停。资本大事1黄光裕为国美提供3次贷款总计5亿港币12月22日晚车企特斯拉股价跌跌不止的背后,是市场转向的转折点吗?近日,大量媒体报道了一件事,就是特斯拉股价继续下跌,市值已经被腾讯控股超越,截止到当地时间12月23日,再度下跌1。76,总市值为3889亿美元。比腾讯当前市值低5亿美元。与202台积电拒绝交付俄罗斯国产芯片,俄部长我们没有工厂能生产俄罗斯的芯片依赖其他企业代工据悉,在俄乌冲突后第一阶段,日韩等国家就响应西方的制裁拒绝向俄罗斯出口半导体材料制成品和芯片技术。对俄芯片封锁是欧美供制供应链上关键的一环,西方宣称俄罗