RFC-0022:说明:结构体成员的默认值 | |
---|---|
状态 | 已接受 |
区域 |
|
说明 | FIDL 结构体成员可以具有默认值。目前,默认值支持功能已部分实现(请参阅下文),此提案旨在阐明默认值应如何运行。 |
作者 | |
提交日期(年-月-日) | 2018-07-17 |
审核日期(年-月-日) | 2018-09-27 |
摘要
FIDL 结构体成员可以具有默认值。目前,默认值支持功能已部分实现(请参阅下文),此提案旨在阐明默认值应如何运行。
设计初衷
- 它可在语言绑定中提供规律性,可防范不一致或意外的使用,并
- 当语言要求显式初始化时,消除繁琐的手动逐成员初始化;
非动机包括:
- 以线格格式保存字节
这不是为了节省线格格式的字节或在编码或解码时节省处理能力。
当前实现
默认值可以使用结构体成员的 FIDL 语言进行表达:
- (+) 支持数值字面量、布尔字面量和字符串字面量。
- (-)
fidlc
不提供对字面量是否可赋值给结构体成员的类型检查。可以将字符串字面量“hello”分配给布尔值,将负数分配给 uint,或将超出边界的数值分配给 int16。 - (-) 语言绑定支持不一致,目前仅适用于 C++ 和 Dart 绑定。不支持 Go、C 或 Rust。
例如(来自 //zircon/system/host/fidl/examples/types.test.fidl):
struct default_values {
bool b1 = true;
bool b2 = false;
int8 i8 = -23;
int16 i16 = 34;
int32 i32 = -34595;
int64 i64 = 3948038;
uint8 u8 = 0;
uint16 u16 = 348;
uint32 u32 = 9038;
uint64 u64 = 19835;
float32 f32 = 1.30;
float64 f64 = 0.0000054;
string s = "hello";
};
设计
可以为结构体成员定义默认值。默认值显示在字段定义的末尾,采用类似 C 语言的 = {value}
模式。
语法
// cat.fidl
enum CatAction : int8 {
SIT = -10;
WALK = 0;
SNEAK = 2;
};
struct Location {
uint8 pos_x = 10; // Position X
uint8 pos_y; // Position Y. Default unspecified. Fall-back to 0
float32 pos_z = 3.14; // Position Z.
float32 pos_t; // Default unspecified. Fall-back to 0.0
};
struct Cat {
string name; // Automatic default to empty string
CatAction action = CatAction::SNEAK;
Location loc;
};
语义
请参阅 RFC-006,其中阐明了默认值的语义以及对绑定的相关要求。
支持的类型
- 基元类型:
bool
、int8
、int16
、int32
、int64
、uint8
、uint16
、uint32
、uint64
、float32
、float64
- 非 null 的
string
、string:N
string:N
应将预留但未使用的内存清零。
不支持的类型
array<T>:N
- 设为零
- 非 null 类型:
vector<T>
、vector<T>:N
- 设为零
- 可为 null 的类型:
string?
、string:N?
、vector<T>?
、vector<T>:N?
- 设为 null
handle
struct
- 虽然
struct
中的每个成员都可能具有默认值,但struct
本身没有默认值。
- 虽然
union
- 为避免任何冲突,系统会忽略
union
成员的任何默认值,或union
的任何深度的子结构的默认值。
- 为避免任何冲突,系统会忽略
默认值的细微差别
重点是值本身,而不是分配值的方式。这至少说明了两点:
- 无论是由于其他机制明确分配了感兴趣的参数而使用默认值,还是未使用默认值,都没有区别。
- 在序列化或反序列化时,没有额外的(透明)逻辑层来分配值。
实现
以下是 C、Rust 和 Go 绑定的实现示例
// in FIDL "default.fidl"
struct Location {
uint8 pos_x = 10;
uint8 pos_y = 20;
uint8 pos_x; // Should be set to "zero" according to above.
};
// C binding "defaults/fidl.h"
typedef struct _Location_raw {
uint8_t pos_x;
uint8_t pos_y;
uint8_t pos_z
} Location;
Location Location_default = { 10, 20, 0 }; // Or in the source file.
// May be used for memcmp,
memcpy, etc.
#define Location(my_instance) Location my_instance = Location_default;
// C code "example.c"
#include <fidl.h>
void showme(Location loc) {
printf("(%u, %u, %u)\n", loc.pos_x, loc.pos_y, loc.pos_z);
}
int main() {
Location(alpha);
Location beta;
Location gamma = Location_default;
showme(alpha); showme(beta); showme(gamma);
return 0;
}
// Rust binding
struct Location {
pos_x: u8,
pos_y: u8,
pos_z: u8,
}
impl std::default::Default for Location {
fn default() -> Self { Self { pos_x: 10, pos_y: 20, pos_z: 0 } }
}
// Go binding, using export control
type location struct {
pos_x uint8
pos_y uint8
pos_z uint8
}
Func NewLocation() location {
loc := location{}
loc.pos_x = 10
loc.pos_y = 20
// loc.pos_z = 0 Maybe omitted.
return loc
}
性能
不适用
安全
不适用
测试
不适用
向后兼容性
此更改会导致 FIDL 文件源向后不兼容。无需更改 ABI 或线格格式。
缺点、替代方案和未知情况
我们尚未评估在所有语言绑定中实现此功能是否会很简单。
在先技术和参考文档
协议缓冲区、平面缓冲区提供默认值。Golang 有一个零值的概念,其中声明且没有明确初始值的变量会被明确初始化为零。
开源方法:
// From https://github.com/creasty/defaults
type Sample struct {
Name string `default:"John Smith"`
Age int `default:"27"`
Gender Gender `default:"m"`
Slice []string `default:"[]"`
SliceByJSON []int `default:"[1, 2, 3]"` // Supports JSON format
Map map[string]int `default:"{}"`
MapByJSON map[string]int `default:"{\"foo\": 123}"`
Struct OtherStruct `default:"{}"`
StructPtr *OtherStruct `default:"{\"Foo\": 123}"`
NoTag OtherStruct // Recurses into a nested struct even without a tag
OptOut OtherStruct `default:"-"` // Opt-out
}