首页
统计
留言板
关于
Search
1
Rust 包装 objc Block
166 阅读
2
Flutter 调用 Rust 生成的 共享/静态 库
157 阅读
3
02. Rust 内存管理 Copy & Clone(上)
157 阅读
4
用 Rust 开发 iOS 应用(粗糙版)
104 阅读
5
纯 C 写个 iOS App(误)
99 阅读
默认分类
Rust
Apple
iOS
Swift
Android
emulator
NES
登录
/
注册
Search
标签搜索
Rust
iOS
NES
Swift
Android
杂项
limit
累计撰写
18
篇文章
累计收到
0
条评论
首页
栏目
默认分类
Rust
Apple
iOS
Swift
Android
emulator
NES
页面
统计
留言板
关于
搜索到
11
篇与
的结果
2021-02-23
Rust 数据结构-动手实现 Vec
正文讲屁话阶段Rust 的 Vec 其实是动态数组, 很多语言内置动态数组, 譬如 JavaScript Python这类, 像 Rust 这种具有内存控制能力的语言, 就选择了标准库内置动态数组.基本用法let mut v1 : Vec<i32> = vec![]; dbg!(v1.len(), v1.capacity()); for i in 1..10 { v1.push(i); } dbg!(&v1);布局现在直接写一下 Rust 动态数组直观实现pub struct MyVec<T> { ptr: *mut T, cap: usize, len: usize, }这样直接编译能通过, 但是 ptr 这个裸指针不能让 Drop check 正常工作, 因为直接在这里使用裸指针 Drop 检查器会认为你没有持有任何值, 因此我们可以用 Unique<T> 处理祼指针 *mut T, 通过它内部的 PhantomData 来帮助 Drop check 工作.Unique<T> 可以封装祼指针T 是可变的可以进行 drop 检查T 实现了 Send/Sync, 该指针也具备 Send/Sync 特性, 等于讲线程安全指针具备非空性既然如此自己实现一个吧, 解析源码自己写不会浪费什么时间, 还能体会别人的设计, 顺道实现一下 Send/Syncuse std::marker::PhantomData; struct MyUnique<T: ?Sized> { ptr: *const T, _marker: PhantomData<T>, } unsafe impl<T: Send + ?Sized> Send for MyUnique<T> {} unsafe impl<T: Sync + ?Sized> Sync for MyUnique<T> {} impl<T: ?Sized> MyUnique<T> { #[inline] pub fn new(ptr: *mut T) -> Option<Self> { if !ptr.is_null() { Some(unsafe { MyUnique { ptr: ptr as _, _marker: PhantomData, } }) } else { None } } #[inline] pub const fn as_ptr(&self) -> *mut T { self.ptr as *mut T } }我们看到有个 ?Sized 的 trait bound 这玩意其实是指定一下泛型的特性, 意思是指编译时确定大小, 默认情况下直接写 T 就是 Sized 了, 加了个问号就会放宽约束的范围, 编译时不确定大小的也被接受. 加上 #[inline] 这个属性就表示内联函数, 因为这几个函数可能会经常使用到, 内联会带点加速效果.内存分配之后我们要考虑初始化容器了, 如果容器放了东西肯定会开辟内存空间, 但是初始化的情况容器应该是空的, 既然是空的肯定不会分配内存, 那就用 MyUnique 建个空的东西imp<T: ?Sized> MyUnique<T> { #[inline] pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { MyUnique { ptr: ptr as _, _marker: PhantomData, } } } impl<T> MyUnique<T> { pub const fn empty() -> Self { unsafe { MyUnique::new_unchecked(mem::align_of::<T>() as *mut T) } } } pub struct MyVec<T> { ptr: MyUnique<T>, cap: usize, len: usize, } impl<T> MyVec<T> { fn new() -> Self { assert_ne!(mem::size_of::<T>(), 0, "We're not ready to handle ZSTs"); MyVec { ptr: MyUnique::empty(), len: 0, cap: 0, } } }接下来写一下内存分配相关的代码, 既然我们需要分配内存, 这块其实没什么东西, 就是读一下内存分配的文档然后使用起来, 当然也要考虑对齐. 其实这是个扩容处理use std::alloc::{handle_alloc_error, realloc, Layout}; use std::mem; impl<T> MyVec<T> { fn grow(&self) -> Self { unsafe { let (new_cap, ptr) = if self.cap == 0 { let ptr = alloc(Layout::array::<T>(1).unwrap()); (1, ptr) } else { let new_cap = self.cap * 2; let layout = Layout::array::<T>(self.cap).unwrap(); let ptr = realloc(self.ptr.as_ptr() as *mut _, layout, layout.size()); if ptr.is_null() { handle_alloc_error(Layout::from_size_align_unchecked( new_cap * elem_size, mem::align_of::<T>(), )); } (new_cap, ptr) }; Self { ptr: MyUnique::new_unchecked(ptr as *mut _), cap: new_cap, len: self.len, } } } }push & pop上面已经做到能分配内存了, 接下来自然是实现基本的功能.先分析一波 push 的行为, push 就是往动态数组添加元素, 如果满了就需要重新分配内存, 其实就是调用 grow, 每次添加元素后长度也要相应 +1, 还需要对相应地址写元素. 写入行为用 std::ptr 的 write 函数来处理, 相应的 pop 也很好理解, 只要把最后一个读取出来同时把长度 -1use std::{mem, ptr}; impl<T> MyVec<T> { pub fn ptr(&self) -> *mut T { self.ptr.as_ptr() } pub fn push(&mut self, element: T) { if self.len == self.cap { self.grow(); } unsafe { ptr::write(self.ptr().add(self.len), element); } self.len += 1; } pub fn pop(&mut self) -> Option<T> { if self.len == 0 { None } else { self.len -= 1; unsafe { Some(ptr::read(self.ptr().add(self.len))) } } } }回收资源Rust 的机制让我们处理这个问题非常简单, 只需要实现一下 trait Dropimpl<T> Drop for MyVec<T> { fn drop(&mut self) { let elem_size = mem::size_of::<T>(); if elem_size != 0 { while let Some(_) = self.pop() {} unsafe { dealloc(self.ptr() as *mut _, Layout::array::<T>(self.cap).unwrap()); } } } }解引用至此, 已经实现了一个简单的数据结构, 但是我们还没法跟 slice 相通, 真实的 Vec 不是这样的, 所以当下我们应该实现一下自动解引用, 只要实现了下面这些东西, 我们就可以用 slice 的提供的接口了use std::ops::{Deref, DerefMut}; impl<T> Deref for MyVec<T> { type Target = [T]; fn deref(&self) -> &[T] { unsafe { std::slice::from_raw_parts(self.ptr(), self.len) } } } impl<T> DerefMut for MyVec<T> { fn deref_mut(&mut self) -> &mut [T] { unsafe { std::slice::from_raw_parts_mut(self.ptr(), self.len) } } }插入跟删除插入插入的行为其实就是把当前要插入的位置之后的所有元素分别向右移动一位, 譬如一个数组 [1, 2, 3], 我要把 10 插入 索引 1 位置(就是元素 2), 那么 10 的下标就是 1, 同时元素 2 跟 3 的下标就是 2 跟 3, 最后就变成了 [1, 10, 2, 3].impl<T> MyVec<T> { // ... pub fn insert(&mut self, index: usize, element: T) { if self.cap == self.len { self.grow(); } unsafe { if index < self.len { ptr::copy(self.ptr().add(index), self.ptr().add(index + 1), self.len - index); } ptr::write(self.ptr().add(index), element); self.len += 1; } } }删除删除的行为也很好理解, 跟插入反着来就可以了, 只要把要删除的位置之后所有的下标向左移动一位, 譬如现在把之前插入后的数组 [1, 10, 2, 3] 10 给删除掉, 10 所在的下标是 1, 后面的元素的下标分别是 2 跟 3, 后面的 -1 就完成了.impl<T> MyVec<T> { // ... pub fn remove(&mut self, index: usize) -> T { assert!(index < self.len, "index out of bounds"); unsafe { self.len -= 1; let result = ptr::read(self.ptr().add(index)); ptr::copy(self.ptr().add(index + 1), self.ptr().add(index), self.len - index); result } } }IntoIter这趟开始处理一下 Vec 才有的迭代器, 其实只要实现了自动解引用的 trait, 就可以使用 slice 的 iter 还有 iter_mut, 但是 slice 是没有 into_iter 的, 所以我们得实现一下.现在有个问题, 既然已经有了 slice 的迭代器功能了, 我们为什么要实现这个 IntoIter?我们可以看到一个 Vec 可以直接用 for 进行循环遍历, 原因就是只要一个自定义的类型实现了 IntoIter 就具备能被 for 迭代的能力let v = vec![1, 2, 3]; for i in v { dbg!(i); }现在可以用两个指针来处理迭代器的操作, 一个在开头, 一个在结尾后面那一个, 只要开头的指针跟结尾后一个的指针地址相同, 就表明迭代结束了.[1, 2, 3, 4, 5, sth] ^ ^ start end现在来建立个迭代器结构, 大概长这样struct IntoIter<T> { start: *const T, end: *const T, }当然后续还要处理内存相关的, 所以我们应该把 Vec 分配的空间讯息保存一下, 当然还要把 MyVec 转化成 IntoIter 类型struct IntoIter<T> { start: *const T, end: *const T, buf: MyUniuqe<T>, cap: usize, } impl<T> MyVec<T> { fn into_iter(self) -> IntoIter<T> { let MyVec { ptr, cap, len } = self; mem::forget(self); unsafe { IntoIter { buf: ptr, cap, start: ptr.as_ptr(), end: if cap == 0 { ptr.as_ptr() } else { ptr.as_ptr().add(len) }, } } } }还要实现一下迭代器, size_hint 是仿写标准库的, 主要作用是表达剩余可迭代元素数量上下界, 下面是 next 相关的操作 impl<T> Iterator for IntoIter<T> { type Item = T; fn next(&mut self) -> Option<T> { if self.start == self.end { None } else { unsafe { let result = ptr::read(self.start); self.start = self.start.offset(1); Some(result) } } } fn size_hint(&self) -> (usize, Option<usize>) { let len = (self.end as usize - self.start as usize) / mem::size_of::<T>(); (len, Some(len)) } }还有 next_back 的操作 impl<T> DoubleEndedIterator for IntoIter<T> { fn next_back(&mut self) -> Option<T> { if self.start == self.end { None } else { unsafe { self.end = self.end.offset(-1); Some(ptr::read(self.end)) } } } }为了处理内存相关的, 我们要给 IntoIter 实现一下 Drop traitimpl<T> Drop for IntoIter<T> { fn drop(&mut self) { if self.cap != 0 { for _ in &mut *self {} unsafe { dealloc(self.buf.as_ptr() as *mut _, Layout::array::<T>(self.cap).unwrap()); } } } }RawVec现在继续重构代码, 因为我们分别给 IntoIter 跟 MyVec 实现了一遍 Drop, 所以重构一下代码是有必要的pub struct MyVec<T> { buf: RawVec<T>, len: usize, } impl<T> MyVec<T> { pub fn push(&mut self, element: T) { if self.len == self.cap() { self.buf.grow(); } unsafe { ptr::write(self.ptr().add(self.len), element); } self.len += 1; } pub fn pop(&mut self) -> Option<T> { if self.len == 0 { None } else { self.len -= 1; unsafe { Some(ptr::read(self.ptr().add(self.len))) } } } pub fn insert(&mut self, index: usize, element: T) { assert!(index <= self.len, "index out of bounds"); if self.cap() == self.len { self.buf.grow(); } unsafe { if index < self.len { ptr::copy(self.ptr().add(index), self.ptr().add(index + 1), self.len - index); } ptr::write(self.ptr().add(index), element); self.len += 1; } } pub fn remove(&mut self, index: usize) -> T { assert!(index < self.len, "index out of bounds"); unsafe { self.len -= 1; let result = ptr::read(self.ptr().add(index)); ptr::copy(self.ptr().add(index + 1), self.ptr().add(index), self.len - index); result } } } impl<T> Drop for MyVec<T> { fn drop(&mut self) { while let Some(_) = self.pop() {} } } struct IntoIter<T> { start: *const T, end: *const T, _buf: RawVec<T>, } impl<T> Iterator for IntoIter<T> { type Item = T; fn next(&mut self) -> Option<T> { if self.start == self.end { None } else { unsafe { let result = ptr::read(self.start); self.start = self.start.offset(1); Some(result) } } } fn size_hint(&self) -> (usize, Option<usize>) { let len = (self.end as usize - self.start as usize) / mem::size_of::<T>(); (len, Some(len)) } } impl<T> DoubleEndedIterator for IntoIter<T> { fn next_back(&mut self) -> Option<T> { if self.start == self.end { None } else { unsafe { self.end = self.end.offset(-1); Some(ptr::read(self.end)) } } } } impl<T> Drop for IntoIter<T> { fn drop(&mut self) { for _ in &mut *self {} } } struct RawVec<T> { ptr: MyUnique<T>, cap: usize, } impl<T> RawVec<T> { fn new() -> Self { RawVec { ptr: MyUnique::empty(), cap: 0, } } fn grow(&self) -> Self { unsafe { let (new_cap, ptr) = if self.cap == 0 { let ptr = alloc(Layout::array::<T>(1).unwrap()); (1, ptr) } else { let new_cap = self.cap * 2; let layout = Layout::array::<T>(self.cap).unwrap(); let ptr = realloc(self.ptr.as_ptr() as *mut _, layout, layout.size()); if ptr.is_null() { handle_alloc_error(Layout::from_size_align_unchecked( new_cap * mem::size_of::<T>(), mem::align_of::<T>(), )); } (new_cap, ptr) }; Self { ptr: MyUnique::new_unchecked(ptr as *mut _), cap: new_cap, } } } } impl<T> Drop for RawVec<T> { fn drop(&mut self) { if self.cap != 0 { unsafe { dealloc(self.ptr.as_ptr() as *mut _, Layout::array::<T>(self.cap).unwrap()); } } } }抽取迭代操作现在我们基本的 Vec 结构已经做出来了, 现在仿照之前的 RawVec 做一份封装.struct RawValIter<T> { start: *const T, end: *const T, } impl<T> RawValIter<T> { unsafe fn new(slice: &[T]) -> Self { RawValIter { start: slice.as_ptr(), end: if slice.len() == 0 { slice.as_ptr() } else { slice.as_ptr().offset(slice.len() as isize) }, } } } impl<T> Iterator for RawValIter<T> { type Item = T; fn next(&mut self) -> Option<T> { if self.start == self.end { None } else { unsafe { let result = ptr::read(self.start); self.start = self.start.offset(1); Some(result) } } } fn size_hint(&self) -> (usize, Option<usize>) { let len = (self.end as usize - self.start as usize) / mem::size_of::<T>(); (len, Some(len)) } } impl<T> DoubleEndedIterator for RawValIter<T> { fn next_back(&mut self) -> Option<T> { if self.start == self.end { None } else { unsafe { self.end = self.end.offset(-1); Some(ptr::read(self.end)) } } } }然后改造一下迭代器, 现在只要在各函数内部调用 RawValIter 的实现就行了struct IntoIter<T> { _buf: RawVec<T>, iter: RawValIter<T>, } impl<T> Drop for MyVec<T> { fn drop(&mut self) { while let Some(_) = self.pop() {} } } impl<T> Iterator for IntoIter<T> { type Item = T; fn next(&mut self) -> Option<T> { self.iter.next() } fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } } impl<T> DoubleEndedIterator for IntoIter<T> { fn next_back(&mut self) -> Option<T> { self.iter.next_back() } } impl<T> Drop for IntoIter<T> { fn drop(&mut self) { for _ in &mut self.iter {} } }处理 Zero-Sized Types通常情况下, Rust 是不需要处理 Zero-Sized Types 的, 但是现在我们的代码中有大量关于裸指针的操作, 假如给分配器传递 zst, 会导致未定义行为, 对 zst 裸指针进行 offset 是一个 no-op 行为.先把 new 函数的 cap 处理一下, 如果是 size_of 处理出来的 T 是 0 的情况, 就给 0 按位取反(usize::MAX), 因为 T 的 size_of 为 0 其实不需要开辟内存, 反正你存进来的都是 0, 逻辑上不会占用内存.impl<T> RawVec<T> { fn new() -> Self { let cap = if mem::size_of::<T>() == 0 { !0 } else { 0 }; RawVec { ptr: MyUnique::empty(), cap, } } }然后就是 grow 函数也处理一下impl<T> RawVec<T> { // ... fn grow(&self) -> Self { unsafe { let elem_size = mem::size_of::<T>(); assert_ne!(elem_size, 0, "capacity overflow"); let (new_cap, ptr) = if self.cap == 0 { let ptr = alloc(Layout::array::<T>(1).unwrap()); (1, ptr) } else { let new_cap = self.cap * 2; let layout = Layout::array::<T>(self.cap).unwrap(); let ptr = realloc(self.ptr.as_ptr() as *mut _, layout, layout.size()); (new_cap, ptr) }; if ptr.is_null() { handle_alloc_error(Layout::from_size_align_unchecked( new_cap * elem_size, mem::align_of::<T>(), )); } Self { ptr: MyUnique::new_unchecked(ptr as *mut _), cap: new_cap, } } } }RawVec 的 Drop 也需要处理, 其实之前的实现也可以, 但是假装对齐一下吧!impl<T> Drop for RawVec<T> { fn drop(&mut self) { let elem_size = mem::size_of::<T>(); if self.cap != 0 && elem_size != 0 { unsafe { dealloc( self.ptr.as_ptr() as *mut _, Layout::from_size_align_unchecked(self.cap * elem_size, mem::align_of::<T>()), ); } } } }然后是 RawValIter 的 zst 处理impl<T> RawValIter<T> { unsafe fn new(slice: &[T]) -> Self { RawValIter { start: slice.as_ptr(), end: if mem::size_of::<T>() == 0 { ((slice.as_ptr() as usize) + slice.len()) as *const _ } else if slice.len() == 0 { slice.as_ptr() } else { slice.as_ptr().offset(slice.len() as isize) }, } } }迭代器也处理一下, size_hint 除数为 0 的情况需要处理impl<T> Iterator for RawValIter<T> { type Item = T; fn next(&mut self) -> Option<T> { if self.start == self.end { None } else { unsafe { let result = ptr::read(self.start); self.start = if mem::size_of::<T>() == 0 { (self.start as usize + 1) as *const _ } else { self.start.offset(1) }; Some(result) } } } fn size_hint(&self) -> (usize, Option<usize>) { let elem_size = mem::size_of::<T>(); let len = (self.end as usize - self.start as usize) / (if elem_size == 0 { 1 } else { elem_size }); (len, Some(len)) } } impl<T> DoubleEndedIterator for RawValIter<T> { fn next_back(&mut self) -> Option<T> { if self.start == self.end { None } else { unsafe { self.end = if mem::size_of::<T>() == 0 { (self.end as usize - 1) as *const _ } else { self.end.offset(-1) }; Some(ptr::read(self.end)) } } } }with_capacity这个实现一下差不多算结束了, 先把 MyVec 改一下impl<T> MyVec<T> { // ... pub fn with_capacity(capacity: usize) -> MyVec<T> { MyVec { buf: RawVec::with_capacity(capacity), len: 0 } } }然后给 RawVec 添加个接口impl<T> RawVec<T> { fn new() -> Self { let cap = if mem::size_of::<T>() == 0 { !0 } else { 0 }; RawVec { ptr: MyUnique::empty(), cap, } } pub fn with_capacity(cap: usize) -> Self { RawVec::allocate_in(cap, None) } fn allocate_in(cap: usize, p: Option<MyUnique<T>>) -> Self { unsafe { let elem_size = mem::size_of::<T>(); assert_ne!(elem_size, 0, "capacity overflow"); let (new_cap, ptr) = if cap == 0 { let ptr = alloc(Layout::array::<T>(1).unwrap()); (1, ptr) } else { if let Some(some_p) = p { let new_cap = cap * 2; let layout = Layout::array::<T>(cap).unwrap(); let ptr = realloc(some_p.as_ptr() as *mut _, layout, layout.size()); (new_cap, ptr) } else { let ptr = alloc(Layout::array::<T>(cap).unwrap()); (cap, ptr) } }; if ptr.is_null() { handle_alloc_error(Layout::from_size_align_unchecked( new_cap * elem_size, mem::align_of::<T>(), )); } Self { ptr: MyUnique::new_unchecked(ptr as *mut _), cap: new_cap, } } } fn grow(&self) -> Self { RawVec::allocate_in(self.cap, Some(self.ptr)) } }现在已经把 grow 抽取出来, 同时给 with_capacity 调用Drain目前待定吧, 这个稍微麻烦点, 还没想好怎么讲.Git 托管最后把代码放上来https://e.coding.net/limitLiu/algorithms-with-rust.git
2021年02月23日
18 阅读
0 评论
0 点赞
2019-01-14
用 Rust 开发 iOS 应用(粗糙版)
把环境搞定在搞事情之前, 我们先把 Rust 环境配好, 这个很简单, 直接用官网的这条命令.curl https://sh.rustup.rs -sSf | sh随便装一个版本, 稳定版也好, 变态版(beta) 也罢. 然后装上一些工具链, 在终端输入rustup target add aarch64-apple-ios x86_64-apple-ios我个人只装了针对 A7 以上 64 位处理器的工具链, x86_64-apple-ios 这个是给模拟器用的. 还有其他几个工具链, 有需要的也可以装上.rustup target add armv7-apple-ios armv7s-apple-ios i386-apple-ios建个 Rust 项目先现在先建个 Rust 项目, 只要使用 cargo 就好了, 直接在终端输入mkdir rust-on-ios && cd rust-on-ios cargo new rs --lib mkdir ios现在可以看到 rust-on-ios 目录下有 ios 和 rs 文件夹. 打开 rs 文件夹 src 目录下的 lib .rs 文件, 先搞个 "hello world" 试一下效果.use std::os::raw::{c_char}; use std::ffi::{CString}; #[no_mangle] pub extern fn say_hello() -> *mut c_char { CString::new("Hello Rust").unwrap().into_raw() }姑且就写这个. 这里的 #[no_mangle] 必须要写, 这个是保证编译后能找到这个函数. 然后我们来建个头文件, 因为之后会把 Rust 项目编译成库文件, 所以搞个 .h 文件提供接口.char *say_hello(void);还差一步, 我们现在要修改一下 Cargo.toml 文件, 到时候把 Rust 源码编译成库.[package] name = "rs" version = "0.1.0" authors = ["limit <747638920@qq.com>"] edition = "2018" publish = false [lib] name = "app" crate-type = ["staticlib"]现在我们到 rs 目录下编译一下这个项目.cargo build --target x86_64-apple-ios --release编译好之后, 你会在 target/x86_64-apple-ios 目录下发现一个 libapp.a 文件. 接下来建个 iOS 项目.创建 iOS 项目现在来创建个 Single Page App 项目. 我图个省事, 这里直接建 Objective-C 项目, 要建 Swift 项目也可以, 不过需要搞桥接. 一路 Next 创建了项目, 然后添加 lib 文件这个 libapp.a 是我们用 Rust 项目编译好的文件, 这个 libresolv.tbd 是拿来做链接用的.要想添加 libapp.a, 直接点这个加号, 然后点 Add Other, 然后选中编译好的 libapp.a 文件. 然后把之前写好的头文件放到项目中. 编译的时候发现出错了. 因为我们虽然把 libapp.a 引入到了项目中, 但是编译的时候, 工具并不清楚 lib 文件在哪, 所以我们得手动设置一下 lib 文件的搜索路径.现在再进行一次编译应该就能成功了.为了演示效果, 在 ViewControll.m 文件中使用一下这个函数吧.#import "ViewController.h" #import "libapp.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSString *fromCStr = @(say_hello()); UILabel *label = [[UILabel alloc] initWithFrame: (CGRect) { 100, 100, 100, 100 }]; label.text = fromCStr; [self.view addSubview:label]; } @end然后模拟器上应该显示了 Hello Rust 这段文字.要想用 Rust 开发移动端应用需要 Rust FFI 相关的知识, 后续我应该会写点 Rust FFI 的相关内容, 再配合 Flutter 写 UI, 开发体验肯定 up.
2019年01月14日
104 阅读
0 评论
0 点赞
2018-12-17
03. Rust 内存管理 Copy & Clone(下)
Copy之前讲到 Rust 有部分类型是默认实现了 std::marker::Copy trait 的.Rust 有很多类型, 有 整型, 浮点型, 布尔型 和 字符型, 还有 元组, 数组, 此外还有结构体, 枚举类型, & 借用指针, &mut 可变借用指针, 还有标准库提供的类型...默认实现了 Copy 的类型像 整型, 浮点型, 布尔型, 字符型, 都是实现了 Copy trait 的, 元组类型, 如果某个元组内的值都实现了 Copy trait, 那这个元组也是 impl Copy 类型, 数组同理.需要手动实现 Copy 的类型Rust 的结构体, 枚举类型, 如果它们的内部都是 impl Copy 的, 那么它们也可以自己手动 impl Copy.无法实现 Copy 的类型Box 就是无法实现 Copy 的类型, 原因很简单, 如果 Box 可以实现 Copy, 那么就会有多次释放这类问题. 还有可变借用指针的类型 &mut T, 同样的理由.CloneRust 有些类型是实现了 std::clone::Clone trait 的. 实现了这个 trait 就可以有 clone 函数. 这个 trait 还有一个 clone_from 函数, 这个函数是有默认实现的.#[stable(feature = "rust1", since = "1.0.0")] #[lang = "clone"] pub trait Clone : Sized { #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "cloning is often expensive and is not expected to have side effects"] fn clone(&self) -> Self; #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn clone_from(&mut self, source: &Self) { *self = source.clone() } }理论上, 我们可以按照自己的要求实现 clone, 对于有 Copy 约束的类型, 实现 Clone trait 需要保证跟 Copy 是相容的, 也就是我们自己实现的 Clone 不会导致 Copy 的行为不正确.通常情况下我们使用 Rust 的 #[derive(Clone)] 自动实现 Clone 就好了, 主要是避免手动实现出错.
2018年12月17日
56 阅读
0 评论
0 点赞
2018-12-15
02. Rust 内存管理 Copy & Clone(上)
CloneRust 语法上一个变量的值是转移给另一个变量, 但是有些情况下可能会想变量值转移之后, 自身还能继续使用. 可以使用 clone 函数let a = String::from("test"); let b = a.clone(); println!("{}", a);clone 这个函数是在标准库的 std::clone::Clone trait 里, 既然是个 trait, 也就意味着可以自己实现一套操作, 通常情况下用默认的定义就好了.Copy我们现在了解到每次绑定变量, 都会发生所有权转移, 但是你会发现写有些东西的时候好像行为跟目前的认知有点不一样.let a: i32 = 10; let b = a; println!("a = {}", a); // a = 10a 没有使用 clone 还能使用, 原因是 Rust 有部分类型默认实现了 std::marker::Copy trait. 像 structs 这类没有默认实现的类型, 想要这样就得实现一下 Copy.fn main() { let p1 = Point { x: 1.0, y: 1.0 }; let p2 = p1; println!("p1 = {:?}", p1); } #[derive(Debug)] struct Point { x: f64, y: f64, } impl Copy for Point {}但是其实这样还是没法用的, 编译后就报错了, 因为 struct Point 没有实现 Clone trait.pub fn main() { let p1 = Point { x: 1.0, y: 1.0 }; let p2: Point = p1; println!("p1 = {:?}", p1); } #[derive(Debug)] struct Point { x: f64, y: f64, } impl Clone for Point { fn clone(&self) -> Self { Self { x: self.x, y: self.y } } } impl Copy for Point {}现在终于好使了. 但是我们发觉做这些操作非常烦, 我们注意到 #[derive(Debug)] 这个东西, 刚好 Rust 提供了Clone, Copy 的属性.pub fn main() { let p1 = Point { x: 1.0, y: 1.0 }; let p2: Point = p1; println!("p1 = {:?}", p1); } #[derive(Debug, Clone, Copy)] struct Point { x: f64, y: f64, }Rust 默认绑定变量是进行 move 行为, 想要保留 move 前的变量, 可以使用 clone 函数, 想要实现基本类型一样的 copy 行为, 我们可以添加 Clone, Copy 属性.
2018年12月15日
157 阅读
0 评论
0 点赞
2018-12-10
01. Rust 内存管理 Ownership & Move
关于所有权通常在 C/Cpp 中, 假设我们用一个指针指向一块申请的内存区域char *p = (char *) malloc(50);假设这个 p 携带了数据, 刚好有个函数需要使用到 p 携带的数据,那么就像这样#include <stdio.h> #include <string.h> void foo(char *p) { *p = NULL; } int main(int argc, char **argv) { char *p = (char *) malloc(50); strcpy(p, "test"); printf("%s\n", p); foo(p); printf("%s\n", p); return 0; }foo 你不看函数实现根本不晓得内部会做什么事, 是否进行了 free 操作等等. 就像这里, 函数内部把指针设成 NULL, 你外部也不晓得. Rust 通过引入一个 ownership 的概念来解决此类问题.Ownership我们可以定义一个变量来理解.let a = String::from("test");一般我们都会讲给变量 a 赋一个 String 类型的值, 但是 Rust 里的说法是, 给 a 绑定一个 String 类型的值, 为什么要这么讲嘞. 首先绑定就是当前情况下, String 类型的这个值, 只有 a 这一个管理者, 所以 a 就是值的所有者, a 离开当前的作用域, a 就被处理掉, 包括它所管理的值. 这就是 ownership 的概念.Move然后就是 move, 这个概念很好理解, 就是变量的 ownership 转移的行为. 我们的变量 a 现在拥有了它的值的所有权, 要是绑定的时候再加个 mut, 还能改变值. 我要是再把它绑定给其他的变量, 自然也是可以的, 但是同时得把关于这个值的 ownership 交出来.let mut a = String::from("test"); a = String::from("foo"); a.push_str("bar"); println!("{}", a); let b = a;现在, 我们的 a 已经没法用了, 也就是 a 的 ownership 已经 move 给 b 了.有一点要搞清爽的就是, 起初 a 是 String 类型值的所有者, 但是当权力变更的时候, a 就没了, 而 String 类型的值直接转交给了 b.Move 在很多地方发生fn main() { let a = foo(); bar(a); } fn bar(s: String) { println!("{}", s); } fn foo() -> String { let a = String::from("bar"); a }这些函数传参, 返回值都会发生 move.好吧, 这次我不会管挖不管埋了, 好好写完这一系列.
2018年12月10日
55 阅读
0 评论
0 点赞
1
2
3