From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from BL2PR02CU003.outbound.protection.outlook.com (mail-eastusazon11011012.outbound.protection.outlook.com [52.101.52.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2333D522EFB; Thu, 15 Oct 2026 12:50:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.52.12 ARC-Seal:i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792068612; cv=fail; b=OQMMubjXUQL9+V/OgdsnNNwDrn0YVsGyjfVrvWszspoY2q6+FPWqxLN7fxJyJfEOKb5mEASLAIfhnRs78CRFoY61BgB5SLPRa9Ugk61VdUUlzIW9D66Zr75qglOoZEICm1V1dgNi59at/BGOjz7xzn2i80j+UwWjXdRVQK0w5zA= ARC-Message-Signature:i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792068612; c=relaxed/simple; bh=V1V569HuMH0nJYDAQDKKRGgddR/ERVxebUYjLM3uCsk=; h=Content-Type:Date:Message-Id:From:To:Cc:Subject:References: In-Reply-To:MIME-Version; b=jZ2FOVHwu/itK8l26uFgyWwEvUmrX1r8IJeT2eRcUJHPSBRSwumzS3mK/rZfYt79p3lMLcs/23dEpzIwGY9CKAyhhgr0+JFV+NHxLiZC++W9GN+oEWnL7KxathDgRSFB/V9zphTvC99JO4h8C61rfpUivt3lULuP9d3iA2rBBtg= ARC-Authentication-Results:i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b=eGoBQfox; arc=fail smtp.client-ip=52.101.52.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b="eGoBQfox" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=SJAl3Db5AdQ6kc3uZ978X8P8DEzYcmNxUp/+boWqAqixRumpG5jlpaK9Dh53wTOql7EVsQI6oftG+PrrN8ftpP34qIiq5ZNmOmqU+KkIK0a+fxiAY3iydlABkdEIhkFVQWAREqCe/UYhK7ylbVZXa2FwfaZbUVsr3eryvdVJpxDb4eKx6EzNFkT7aj/Ck8dqeEhoSI1e5vYgAEyZwwSEWO17jd/tr+upH5obKx0XYL8nfLdwGrPztDMQCxHCl6cOYyy2aJslnsMZnOlUG357e0poezO94PyKEmvSZeSoOrJ9NCszok2RwrFOPdXsQ6DFZiBAZqvDrgmMkDmG0MUyDw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=gyLbfZJaF+b4DEQKf6VxcWquykG3D0VNP4Bj3lRi8oM=; b=i2hHRk8fZHC2P7wP0aEa/dLOIxXMjx5RDUaQ/UaYt80LnUkZr5/nqwwMov/6qMwd4URQIaV4HRK/qzUq6iSIE1SwtQQjw4mvTLdvE35feCY74Dca/iJgAuqth27wkHiX2L9XXA/oakcNDSqt4myQtw48Q6Ehzgbrn2Y1LPM2FBGVdJs/mS5zGxgv/Bs27FIaeG34qPanC+uy06kFXVMg6yoEmvUPY3Ik6ln6h1WCdwDh4LVgt4WpFn0rXLOrg9Mzyqhi03m5CP43zW6URc4xtqtMbpo5ID5zZ7vl6Ua6toZz4+JrOFHpzJYp39UmV4Dpll39fvXIjn5HiXztxok0Tg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com; dkim=pass header.d=nvidia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=gyLbfZJaF+b4DEQKf6VxcWquykG3D0VNP4Bj3lRi8oM=; b=eGoBQfoxD6N21RbnyXVl5XqnyXlbS8NEs8kxoH7uhdPU4QFI0SfcZSWbHI5jclNItdCz2BSwyBMF8rB9vzKUxJs4+IVCWnOlvrmZ9PAYDAFp9ErBdxYzfiKT5orLEkO3oi9N02k1Y7H+ZVKiXMjkJWFE2373qqNuOjS5hGCnDvAFZCd3dhubV84XG+7C5HNlvOLzOxcJiwM20E/VmcKEdt/rRvxi5yLIbFA7U7nO4DBOIdGCv859mABrjOv/V8SCJ25HmGl+hWUejLJAGSUNmYP1icW0ZTWguOBSFuBpOU5l6Vi0ezzhyKRJjeo2dGLwx/bD9Cq/1Bv9MsYG4si0hQ== Authentication-Results: mx.microsoft.com 1; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from DS0PR12MB6413.namprd12.prod.outlook.com (2603:10b6:8:ce::10) by DS7PR12MB6333.namprd12.prod.outlook.com (2603:10b6:8:96::15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.522.13; Thu, 15 Oct 2026 12:50:02 +0000 Received: from DS0PR12MB6413.namprd12.prod.outlook.com ([fe80::e82a:6673:4142:37fa]) by DS0PR12MB6413.namprd12.prod.outlook.com ([fe80::e82a:6673:4142:37fa%5]) with mapi id 15.21.0496.021; Thu, 15 Oct 2026 12:50:02 +0000 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=UTF-8 Date: Thu, 15 Oct 2026 21:49:59 +0900 Message-Id: From: "Eliot Courtney" To: "Alexandre Courbot" , "Eliot Courtney" Cc: "Danilo Krummrich" , "Lorenzo Stoakes" , "Vlastimil Babka" , "Liam R. Howlett" , "Uladzislau Rezki" , "Miguel Ojeda" , "Boqun Feng" , "Gary Guo" , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , "Benno Lossin" , "Andreas Hindborg" , "Alice Ryhl" , "Trevor Gross" , "Daniel Almeida" , "Tamir Duberstein" , =?utf-8?q?Onur_=C3=96zkan?= , "David Airlie" , "Simona Vetter" , "John Hubbard" , "Alistair Popple" , "Timur Tabi" , , , , , "dri-devel" Subject: Re: [PATCH v3 7/8] gpu: nova-core: add NVKV typed decoding X-Mailer: aerc 0.22.0-0-gc2f86b7abde3 References: <20260928-b4-nvkv-v3-0-f04504c262c2@nvidia.com> <20260928-b4-nvkv-v3-7-f04504c262c2@nvidia.com> In-Reply-To: X-ClientProxiedBy: TY4P286CA0127.JPNP286.PROD.OUTLOOK.COM (2603:1096:405:37f::15) To DS0PR12MB6413.namprd12.prod.outlook.com (2603:10b6:8:ce::10) Precedence: bulk X-Mailing-List: rust-for-linux@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: DS0PR12MB6413:EE_|DS7PR12MB6333:EE_ X-MS-Office365-Filtering-Correlation-Id: 3b2397d0-6ea9-4265-25a3-08df2abad413 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|7416014|376014|10070799003|1800799024|23010399003|261009223027099003|56012099006|4143699003|11063799006|260925021311599003|260925021911599003|10067099003|18002099003|22082099003|10063799003|260925022911599003|3023799007; X-Microsoft-Antispam-Message-Info: t/4D/66dEexJNvxdy6S3w5Rs78DYL7Qs+ortEuy+EtqWKz0WogjVV+Z/ssXSgYPNlSRv3uKgEyxtPUkdOO+XFAyVqKhOEfZNwXMiXLX6HanST/J2sym0smTD2wAdAXt1hgkpP5NubnSu9n6z5TJaDI4cJWRI/Yqt0pduv9tv9Bol7g7bzBWJtqnoXzCukkFahd5P9VpG2RPv+fbcqoCozQqciFqHy7IseVtKtrNSdkppEzulKDZsW5kGswWg9h0ocCs7EFTOaADwmUyo+7zHvsEgk1sKLNW0a20noDw8qbX40HeypGmWRl2q6XSOR1feerFZXbTDY9OW56sPYtkH2DtIOw2CDfidMW7oiEFoyB+zbqJ8H5DXSAwDud1djDm/eSLwQtyVstj7Hkr3r8nWSA6ZHtQmUuvxSqxk7W8WyCALIjJwM2pnL7T8nqkN47oUt++ZD3yTiAZaslYEjwheoffcCLjw6wEYjizi5fYm6J27VjpZHQ8SH3UWehJMc00yI3+20E0gso7nsdLvi4/3R6whbzYCr35UyR4ZSR5jd79RbjIfXOTUXQdOt3hAOHyIqyCg9ACAZ8tywiU5C3/gFheSqkdwQHJsx+nfxFj1QLoMygzJyTFo1K5zdGthlFP05VIT+d+azPluScVzZYVT7SFuC1oDA6S8XFuyBvaH5Xo= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:DS0PR12MB6413.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(7416014)(376014)(10070799003)(1800799024)(23010399003)(261009223027099003)(56012099006)(4143699003)(11063799006)(260925021311599003)(260925021911599003)(10067099003)(18002099003)(22082099003)(10063799003)(260925022911599003)(3023799007);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?SzZ2OXBGUTg4VFR3NFpvM0hldnJucXIwWlJRZ28vcFhLalZWNTIvRHpyV3hK?= =?utf-8?B?NTZGNzBKK1VTV0Z5dWxPNE1ZdVdxTkJBaUl1VTA1MHJCZ2hoYnYwc2dVcFlI?= =?utf-8?B?QnJ3b2g3RnpJZVZIaWUrMmJmZHpBdm9pYVdiNHB0bmNmNUNzVTFoVUlNSEZ4?= =?utf-8?B?R3hWQUNxeFdFTzlKTlVXNVJDSWlKV1RodExaVG12RXNuWkRzZUh5UUFIYzZs?= =?utf-8?B?dUxYMFNrY2ZzaXJpaEdzQytwbjdVUjdld0dMOFF2L1QyTTlaUUNLMGo3eGI2?= =?utf-8?B?eGVyYi96bGNnSFc1ZDVqdXNQQ1VUYTlMMGd6U1RBbkVOR3k0R1lmbTJtbEov?= =?utf-8?B?SWdNVDZCNldWSVl3dEJ1VGdPZGh3TmN3RHdETzdGUGtENzF2bU9nVXpsM2hq?= =?utf-8?B?TUpJcnpLbTJXTTNvQW5UOTdvRmd0V05qcDdTdTNENGYyZjY3bkRUOXdtUmhR?= =?utf-8?B?K3h1NHlreTA0MkNaWnlaNnBTZXgyQ2NJemNLNUtlbG8rYlZmQkVPalE2UFdF?= =?utf-8?B?dnY1NlB0QUdDL1BwR21BVmk0SXlVMEtqbzFwdWcxYXVISExXNk9qbVV0ZjFS?= =?utf-8?B?WWVqNk5qU1dkRDZKaW95eGE1L3AvK1NrajZ5VEZDTzAyM3kxUnNRa3JEeVdT?= =?utf-8?B?UVJMbU1mWHpBSHptVkIyTVJWU3J4K2RQMysyWngveVhEZmw0cjNYZjBZaFBp?= =?utf-8?B?a3ZyR3RXOFNzUm9LNGo5REZDeXNrVEVReGlrZi9VK2V4SGVlMU5OY1MwRHY2?= =?utf-8?B?THJjM0dmT3RLQ1h2UE4xVmtmNGloTWhRNmI0NmhHUENyaGVWcVlXUnhkcjJt?= =?utf-8?B?N3NXQVJoT0RYZ21yV08rTUxYa3NYYlNtc3ZLK3czd1FNK3ZyRmtXc0U3MUtl?= =?utf-8?B?ZTVaYkJ6K3VUMzM3UUhXbVB1bnIwdnlyUzNCRUMwUExOZ2pWNVZlYU5BeHMw?= =?utf-8?B?NHZqdllISnBiSXhra2hWV3RFYXNhWnc3VGg1MVJuaVBaY2FPcWZPM0pjMjB2?= =?utf-8?B?NGVjYm9sSjZ6b2M1enVLWXVzOHVuWjAwaHhCS2gzY0p6YTNyNS9uWHR2V0xh?= =?utf-8?B?VWl4d2hPNFlVUlhkekswWWY5SjFEamF6QitTSGRaOVU4aHlRZmdNL3Zudm1a?= =?utf-8?B?ZnRWV1hTT216aXphZE9CNDVRazd1dkhDRGxpeWNYcnA4UzdCUU40N2ZzaHQv?= =?utf-8?B?ekRNbmVVZzJIbERIT0R0TGp3dWJaVHdadUZvUU9RNzRsSVk5by83djJDSlpX?= =?utf-8?B?MGMrK3FZUk5QYTlRb0k0d3oxREd1UmExdVNvZ0tDRUxFQWdCLzhqc29kdGtx?= =?utf-8?B?eXF2eUppcE5GaUVYTXUvb01nZVhqRnVicjVvQkpmM0QybnBqSkRyckhBKysv?= =?utf-8?B?UjVhRzVkbGFtYnpSQjgyNlVsREUxOU5leWIwT20wdTAzSVdDK0NzSXppRCtY?= =?utf-8?B?dThQRWxwcmk0NS9VbE42b2JwQksxREZqdXpRY2gva2FMem9oc0dEWnJaOUEz?= =?utf-8?B?NGN0bEFPVHErdkVOMkQxVm5OSVhuaTZWWmZ4WWhpME1WYTNTVXRRckZ2eWhl?= =?utf-8?B?UUxxUlJBMzBRVUNQUFVSYlNmcjdvQzVyRTBlbis1RGJzRmVEY3c5dFgrVVV1?= =?utf-8?B?bFgyRU1wVENsZkJHWXNTTEorVnlJa0NLNzRiQzhDSU9Sby9RMkU5c2d3MTNo?= =?utf-8?B?cVI4QkgrNnNFUzFzK1VhU1FsQkxmdDc1czZLM2prN1hTREhCZjgvTFd6Y3l2?= =?utf-8?B?N0VGMjJ1bFRvWDlzaUN0RTN4elpGOVJjWDJ2cTFIbTBFL2ZDN0Ixcm82NzlW?= =?utf-8?B?TjJrWDFvaE94eGprOHdtckxNWDB1SmxhZG55d1lrZldYVWlaRjlhTXN5Nmtz?= =?utf-8?B?NHpQdGJsKzQ5VU5jaUFkTzVVa0lTSHBxMUVwTVVOa2ZWSFA0QmhMS09nb2VL?= =?utf-8?B?elp0VDlrTDhhM1FBd1FCN2NCYTlDbUIyd0k5UWNvWFBUY3VpWU9PbFhvQ1lj?= =?utf-8?B?UHlQNXh1VDNhTURkcDVlZGxhaThiWXZ5dUY3MG1CMENxUHowaU0xMWZtNloy?= =?utf-8?B?RmV2d2o5aE1JdVZtTnhmd1dSdExDaXNGdDc5aElLRVNIM2UyM2ZQNFdweDdn?= =?utf-8?B?M0Vab0NueXRIclBudmFyalM2c1BWajVDZ3Q0OXlnL0tLSS9CYStWSnhpRkd3?= =?utf-8?B?emc3SDkvZ2VUNzRocVNFY203TW9mbUlKbG92TGNZd2hzTE94bFNDMmswbGVo?= =?utf-8?B?ZjFNanU5bU9XcExOOXFSS2w3T1Z6bm8xTlloN25rM05tZjVoVU1oUzUyMm41?= =?utf-8?B?Y3RCTFo4UXI2L0NTVXp5Q0pGeVVFbDc5b3VtbWJVblJrS0pZMGdyTDI1TDZu?= =?utf-8?Q?10gnO/58S2URu0WbCTYv6BdhwMi7I2L5jEBbjsRTjfYz8?= X-MS-Exchange-AntiSpam-MessageData-1: Dyu1I6FkhIyN5A== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 3b2397d0-6ea9-4265-25a3-08df2abad413 X-MS-Exchange-CrossTenant-AuthSource: DS0PR12MB6413.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 15 Oct 2026 12:50:02.2402 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: aA4LCbigtWCF9fz/dsHK6TXIa+8vfMFiw0wHhvUOTDhShgMfe+l442xed+CjZ0n0NxdH5mlJ7+qbYfi1QlWmkQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DS7PR12MB6333 On Wed Oct 7, 2026 at 3:30 PM JST, Alexandre Courbot wrote: > On Mon Sep 28, 2026 at 5:42 PM JST, Eliot Courtney wrote: >> Similar to the typed encoding layer, add some decoding type machinery. >> Add a simple macro `nvkv_decode!` which implements `Schema` for a struct >> by composing visit calls to each member. Add some common `Schema` kinds, >> such as `Array` which collects an array value into a fixed maximum size >> array, and `Required` which fails a decode if the value is not sent. >> >> Signed-off-by: Eliot Courtney >> --- >> drivers/gpu/nova-core/gsp/nvkv.rs | 11 +- >> drivers/gpu/nova-core/gsp/nvkv/decode.rs | 622 ++++++++++++++++++++++++= ++++++- >> 2 files changed, 628 insertions(+), 5 deletions(-) >> >> diff --git a/drivers/gpu/nova-core/gsp/nvkv.rs b/drivers/gpu/nova-core/g= sp/nvkv.rs >> index 7ac3a459a98b..5791df07a7fa 100644 >> --- a/drivers/gpu/nova-core/gsp/nvkv.rs >> +++ b/drivers/gpu/nova-core/gsp/nvkv.rs >> @@ -9,7 +9,7 @@ >> //! function calls will map to some struct - for example, f(GPU_NAME_ST= RING_KEY, 0, b"some gpu") >> //! naturally maps to storing a &str with the GPU name. >> =20 >> -#![expect(unused_imports)] >> +#![cfg_attr(not(CONFIG_KUNIT), expect(unused_imports))] >> #![cfg_attr(not(CONFIG_KUNIT), expect(unused_macros))] >> =20 >> use core::{ >> @@ -23,7 +23,8 @@ >> use kernel::{ >> alloc::{ >> allocator::KVmalloc, >> - Allocator, // >> + Allocator, >> + ArrayVec, // >> }, >> bitfield, >> num::Bounded, >> @@ -148,6 +149,12 @@ fn default() -> Self { >> } >> } >> =20 >> +/// A schema field for an array value under the NVKV key `KEY_ID`. >> +#[repr(transparent)] >> +pub(crate) struct Array { >> + vec: ArrayVec, >> +} > > I still don't see why this needs to be declared in this module when no > encoder element ever uses it, and the module reexports everything from > `decode` anyway. Can this be moved next to the other schema kinds? This will be used soon [1] for encoding. The convention I have been using is if a type is used for both encoding and decoding, I put it in nvkv.rs (since it doesn't fit obviously in either - the encoding/decoding related impls can go in there though). If we put IndexedKey in nvkv.rs as pub crate since we'll use it soon, it's consistent to keep Array here as well. WDYT? [1]: https://lore.kernel.org/all/473ff4cbabf401357934a611c0310499130d9efe.1= 790580105.git.zhiw@nvidia.com/ > >> + >> bitfield! { >> /// The op word that starts each NVKV operation. >> struct Op(u64) { >> diff --git a/drivers/gpu/nova-core/gsp/nvkv/decode.rs b/drivers/gpu/nova= -core/gsp/nvkv/decode.rs >> index c4c24fe1108e..24dad31296cb 100644 >> --- a/drivers/gpu/nova-core/gsp/nvkv/decode.rs >> +++ b/drivers/gpu/nova-core/gsp/nvkv/decode.rs >> @@ -3,11 +3,22 @@ >> =20 >> #![cfg_attr(not(CONFIG_KUNIT), expect(dead_code))] >> =20 >> -use kernel::prelude::*; >> +use core::{ >> + convert::Infallible, >> + marker::PhantomData, // >> +}; >> + >> +use kernel::{ >> + alloc::ArrayVec, >> + prelude::*, // >> +}; >> +use pin_init::init_array_from_fn; >> =20 >> use crate::{ >> gsp::nvkv::{ >> + Array, >> Index, >> + Key, >> KeyId, >> Op, >> Opcode, // >> @@ -15,6 +26,353 @@ >> num, // >> }; >> =20 >> +/// Defines a schema struct together with its [`Schema`] and [`Visit`] = implementations that decode >> +/// into `$target`. >> +/// >> +/// Each member of the struct should implement [`Schema`] and [`Visit`]= . For every (key, index, >> +/// value) triple decoded from the NVKV stream, the generated parent `V= isit` implementation will > > nit: let's put backticks around (key, index, value) and link to `Visit` > (and other types mentioned in this doccomment, and possibly elsewhere in > this patch). Done (here and other locations). > > <...> >> +/// A schema field for a key that must be present. >> +/// >> +/// `finish` fails with `EINVAL` if no value arrived for the key. >> +#[repr(transparent)] >> +pub(crate) struct Required(Key, KEY_I= D>); > > I just noticed that the schema field types are conflating several > concepts together. On the one hand, you have types associating a key to > some kind of storage (Key, Array, Indexed - let's call these leaf > types), and on the other what looks like modifiers on said leaf types > (Accumulated). > > Which leaves `Required` somewhere in between, as it takes a key > parameter and can only be applied to value types, i.e. you currently > cannot have a `Required>`. But `Accumulated>` does > work IIUC. > > So I think `Required` should work the same way as `Accumulated`, i.e. > just wrap it around the type you want to make required, instead of > switching the leaf type. > > And for symmetry, we should also have an `Optional` wrapper type, so we > can also make fields optional even if they are not `Key`s instead of > having specific behavior for `Key