nocopy 结构体
为什么需要
普通结构体可以随意复制。但如果结构体里装的是文件句柄、网络连接这类只能关闭一次的资源,复制就会出问题:两个副本都以为自己拥有同一个文件,各自关闭一次——第二次关掉的可能是程序后来打开的另一个文件。
用 bu_kaobei(不可拷贝 / nocopy)修饰的结构体禁止复制,并且可以定义一个销毁前自动执行的清理方法。
基本写法
cpro
wailian hs close(fd z32) z32
bu_kaobei jiegouti File {
fd leixing z32,
}
shixian File:f {
hs xiaohui_qian() kongbai {
bu_anquan { close(f.fd) }
}
}cpro
外联 函数 close(fd i32) i32
不可拷贝 结构体 File {
fd 类型 i32,
}
实现 File:f {
函数 销毁前() void {
不安全 { close(f.fd) }
}
}cpro
extern fn close(fd i32) i32
nocopy struct File {
fd type i32,
}
impl File:f {
fn before_destroy() void {
unsafe { close(f.fd) }
}
}xiaohui_qian(销毁前 / before_destroy)是固定的方法名:没有参数,返回类型是 kongbai(void)。变量离开作用域时,编译器会自动调用它:
cpro
hs work() kongbai {
bl f leixing File = { fd: 3 }
// 使用 f
} // 这里自动调用清理方法,文件被关闭cpro
函数 work() void {
变量 f 类型 File = { fd: 3 }
// 使用 f
} // 这里自动调用清理方法,文件被关闭cpro
fn work() void {
var f type File = { fd: 3 }
// 使用 f
} // 这里自动调用清理方法,文件被关闭不能手动调用这个方法。
- 只写
bu_kaobei不写清理方法也可以,那就只是一个不能复制的类型 - 普通结构体不能定义清理方法,否则它仍然能被复制,问题依旧
能怎么用
nocopy 的值不能复制,也不能移动,只能这样用:
cpro
bl f leixing File = { fd: 3 } // ✓ 创建
bl g = f // ✗ 不能复制
bl h = yidong f // ✗ 不能移动cpro
变量 f 类型 File = { fd: 3 } // ✓ 创建
变量 g = f // ✗ 不能复制
变量 h = 移动 f // ✗ 不能移动cpro
var f type File = { fd: 3 } // ✓ 创建
var g = f // ✗ 不能复制
var h = move f // ✗ 不能移动让别的函数用一下,借给它:
cpro
hs read_line(f &File) zifuchuan {
fanhui "……"
}
rukou() {
bl f leixing File = { fd: 3 }
dayin(read_line(&f)) // ✓ 借用,f 仍然归自己
}cpro
函数 read_line(f &File) string {
返回 "……"
}
入口() {
变量 f 类型 File = { fd: 3 }
打印(read_line(&f)) // ✓ 借用,f 仍然归自己
}cpro
fn read_line(f &File) string {
return "……"
}
rukou() {
var f type File = { fd: 3 }
print(read_line(&f)) // ✓ 借用,f 仍然归自己
}让函数创建一个返回出来,必须在 fanhui 处直接构造:
cpro
hs open_it(n z32) File {
fanhui { fd: n } // ✓ 直接构造
}
hs open_bad(n z32) File {
bl t leixing File = { fd: n }
fanhui t // ✗ 不能返回局部变量
}cpro
函数 open_it(n i32) File {
返回 { fd: n } // ✓ 直接构造
}
函数 open_bad(n i32) File {
变量 t 类型 File = { fd: n }
返回 t // ✗ 不能返回局部变量
}cpro
fn open_it(n i32) File {
return { fd: n } // ✓ 直接构造
}
fn open_bad(n i32) File {
var t type File = { fd: n }
return t // ✗ 不能返回局部变量
}按值传参同样不行,请改成借用:
cpro
hs take(f File) kongbai { } // ✗
hs take(f &File) kongbai { } // ✓cpro
函数 take(f File) void { } // ✗
函数 take(f &File) void { } // ✓cpro
fn take(f File) void { } // ✗
fn take(f &File) void { } // ✓什么时候自动清理
以下几种情况都会自动调用清理方法:
- 正常执行到作用域结束
- 提前
fanhui - 错误向外传播时(
sp_kecuo,或在buhuo里返回错误) - 给变量重新赋值时,先清理旧值
同一作用域里有多个 nocopy 变量时,按声明的相反顺序清理。
程序 panic 时不会清理,进程直接结束,由操作系统回收资源。
清理方法里的限制
清理方法里可以读写字段、调用普通函数,但不能把自己或字段借出去,包括调用自己的其他方法:
cpro
shixian File:f {
hs flush() kongbai { }
hs xiaohui_qian() kongbai {
f.flush() // ✗ 不能调用自己的其他方法
bu_anquan { close(f.fd) } // ✓
}
}cpro
实现 File:f {
函数 flush() void { }
函数 销毁前() void {
f.flush() // ✗ 不能调用自己的其他方法
不安全 { close(f.fd) } // ✓
}
}cpro
impl File:f {
fn flush() void { }
fn before_destroy() void {
f.flush() // ✗ 不能调用自己的其他方法
unsafe { close(f.fd) } // ✓
}
}清理动作请直接写在清理方法里。
重要数据要显式关闭
自动清理时如果出错(比如磁盘满了),错误会被静默忽略——清理方法没有返回值,也没有地方报错。
所以写入重要数据时,请调用资源自带的关闭函数并检查结果,例如文件模块的 fs.close(&f),见 fs 文件。自动清理负责"不泄漏",显式关闭负责"确认成功"。
当前不支持
nocopy 类型目前不能用在这些地方:
- 普通结构体的字段(nocopy 结构体自己的字段可以是任何类型)
- 数组元素
- optional
- 闭包捕获
- 全局变量
dayin打印xiaohui提前销毁——请用资源自带的关闭函数
