From nobody Fri Dec 19 20:23:49 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1733748762; cv=none; d=zohomail.com; s=zohoarc; b=EVU8g6PDmyF6Dbgun56XI37w7A7hndseSjHA7+fuZcnP1+z2ebh/lKMJecyhvGNuPU8iqj5hWr9++jukFZcZ7ZYNAiYTsIdiLLhd2X501i8kCXujq8bbw7mUtLMYVIz8juqurwNwn3yhbbCUC8o/1a0//sMfJzXPTlAyACM6rn8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1733748762; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=T6KMN/KKwz/drY0m0B/GAzuPFwO/9IuBICqSOgF3wlY=; b=jHm/uS6kbcQn/wR8fhEvfzv5hXqNyAAEVWojxHoeHq05wFhZaSLFQ3hf35qKSAgO9BVAMMS8pYxlGZIDvM0MbbeIZP1vKjlgKbNhoOJ1FNhYsg8/DbBYtHcVLocf1GajY1S1u6bxrs40+frHDkL2IYz904D6TLJWhzaVoouNM6U= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1733748762327545.117793203575; Mon, 9 Dec 2024 04:52:42 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tKd6X-0008CP-6a; Mon, 09 Dec 2024 07:43:17 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tKd22-0004Yy-HA for qemu-devel@nongnu.org; Mon, 09 Dec 2024 07:38:42 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tKd1w-0007as-Cp for qemu-devel@nongnu.org; Mon, 09 Dec 2024 07:38:33 -0500 Received: from mail-wr1-f70.google.com (mail-wr1-f70.google.com [209.85.221.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-529-5SkZ6bASP8uC9z-rmISFqw-1; Mon, 09 Dec 2024 07:38:25 -0500 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-3862c67763dso1816110f8f.3 for ; Mon, 09 Dec 2024 04:38:25 -0800 (PST) Received: from [192.168.10.47] ([151.81.118.45]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-434f0d6a825sm71952995e9.20.2024.12.09.04.38.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 09 Dec 2024 04:38:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1733747906; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=T6KMN/KKwz/drY0m0B/GAzuPFwO/9IuBICqSOgF3wlY=; b=KjystAASfB/h9av5tZ4PqlUtqDOIdjs5sFHfASFI6EqzOxY3DnpOoUOI4zQWk+o9JqSK8F 3DD4IdZHVgO5yepdKJEyTTgnZOEvNjeQBRNG/k/Lv8wwPoa+U5M5FwRmOG9dgmh1MkJzgx VIm4k+fG7Bjbc8EDOy/oaoUCJgxHHYY= X-MC-Unique: 5SkZ6bASP8uC9z-rmISFqw-1 X-Mimecast-MFC-AGG-ID: 5SkZ6bASP8uC9z-rmISFqw X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1733747903; x=1734352703; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=T6KMN/KKwz/drY0m0B/GAzuPFwO/9IuBICqSOgF3wlY=; b=DoQNFmnxVkZyq84e06+Gp2s+cSEdkDSh16j4+S5nQyeE3FOe5atwpMrxtaf/7oqL00 zN0htOhSB/qAeNW1e8SBrU7PyhKq9I71JUlIzR7YweZMu/emFgmbJnmoLv4MZ8yXBKOE FZQpwxOTBnwzQQW1r7vdQAc0w+zkcsneKL11xnYo6+SKf5GftJCny5gNndzUjLxY0/eM W6OGY8pxfRMX+OXe36OqKlKeJehX1mGJWmcFvpn7JuvODAbmrEE3Xd+TS190Q+OyRjZe JhVKidkVEMRzaf2ZtFTdj61ct0UQ5rDilyAUIuYIvobC2nCQug1RPwcQY/DHPMh3tVq5 Un+Q== X-Gm-Message-State: AOJu0YwW6TR622/DsIfJO/wGENb/Tfsii523q/iF25Effi29ctFqzAR3 9f6/Z9RFrOFa3/pCnomJYNuJURaCgGWKUUlI+hOPmc2neTbLmlQW4g96eZzN1lpVbhBoLyk4C3u vbRj+rvGBHEW+Kk4CUnU/O7190/nqz1VdMzJaGT6on9i2kQvW7vRbapMWqxbXEmkXZA/RmRPiEO 1xd7dIFb0TJLjHlQAL7z86cNFTCWozJnvZTvhe X-Gm-Gg: ASbGnct+stPTvPYTKvNNqnW0QoCcXcn3gUTELjisBmA6Z9V9T6J4H4UcUZm/iMCed9u R54jebc4HRwIsvVI6HW03kTmnW6FBQxLwHgfYS4jRn5yFVW3R8J8j3wkcWIfh8z35OnpMzF/sae Qzvy+c2Ng+Rc33kWWFmi6Et/wzIAbg33/AGLY4mg1cdTanZ8Spz9ZQg6k2L6gYaKOQG5Ht7uGeR ENbnNnnEsVd55haykpvIUGpfRvdVL1tepFrbjid1ZRwpBFOym6uoA== X-Received: by 2002:a05:6000:4715:b0:385:fb34:d585 with SMTP id ffacd0b85a97d-3862b37f1fcmr9529702f8f.34.1733747903513; Mon, 09 Dec 2024 04:38:23 -0800 (PST) X-Google-Smtp-Source: AGHT+IHNIjYvCBUUw5SuI6EdubzMkYBb+V9p2OLC1S6N1gh//MaK5456gKjpwFzm1ivquRIqCgHGWg== X-Received: by 2002:a05:6000:4715:b0:385:fb34:d585 with SMTP id ffacd0b85a97d-3862b37f1fcmr9529680f8f.34.1733747903085; Mon, 09 Dec 2024 04:38:23 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: qemu-rust@nongnu.org, Zhao Liu , Junjie Mao Subject: [PATCH 26/26] rust: callbacks: allow passing optional callbacks as () Date: Mon, 9 Dec 2024 13:37:17 +0100 Message-ID: <20241209123717.99077-27-pbonzini@redhat.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20241209123717.99077-1-pbonzini@redhat.com> References: <20241209123717.99077-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -25 X-Spam_score: -2.6 X-Spam_bar: -- X-Spam_report: (-2.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.489, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, T_SPF_TEMPERROR=0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1733748859389116600 Content-Type: text/plain; charset="utf-8" In some cases, callbacks are optional. Using "Some(function)" and "None" does not work well, because when someone writes "None" the compiler does not know what to use for "F" in "Option". Therefore, adopt () to mean a "null" callback. It is possible to enforce that a callback is valid by adding a "let _: () =3D F::ASSERT_IS_SOME" befo= re the invocation of F::call. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/callbacks.rs | 97 ++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/rust/qemu-api/src/callbacks.rs b/rust/qemu-api/src/callbacks.rs index 6401d807198..83c681d6478 100644 --- a/rust/qemu-api/src/callbacks.rs +++ b/rust/qemu-api/src/callbacks.rs @@ -76,6 +76,31 @@ /// call_it(&move |_| String::from(x), "hello workd"); /// ``` /// +/// `()` can be used to indicate "no function": +/// +/// ``` +/// # use qemu_api::callbacks::FnCall; +/// fn optional FnCall<(&'a str,), String>>(_f: &F, s: &str) ->= Option { +/// if F::IS_SOME { +/// Some(F::call((s,))) +/// } else { +/// None +/// } +/// } +/// +/// assert!(optional(&(), "hello world").is_none()); +/// ``` +/// +/// Invoking `F::call` will then be a run-time error. +/// +/// ```should_panic +/// # use qemu_api::callbacks::FnCall; +/// # fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str) -= > String { +/// # F::call((s,)) +/// # } +/// let s: String =3D call_it(&(), "hello world"); // panics +/// ``` +/// /// # Safety /// /// Because `Self` is a zero-sized type, all instances of the type are @@ -90,10 +115,70 @@ pub unsafe trait FnCall: 'static + Syn= c + Sized { /// Rust 1.79.0+. const ASSERT_ZERO_SIZED: () =3D { assert!(mem::size_of::() =3D= =3D 0) }; =20 + /// Referring to this constant asserts that the `Self` type is an actu= al + /// function type, which can be used to catch incorrect use of `()` + /// at compile time. + /// + /// # Examples + /// + /// ```compile_fail + /// # use qemu_api::callbacks::FnCall; + /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str)= -> String { + /// let _: () =3D F::ASSERT_IS_SOME; + /// F::call((s,)) + /// } + /// + /// let s: String =3D call_it((), "hello world"); // does not compile + /// ``` + /// + /// Note that this use more simply `const { assert!(F::IS_SOME) }` in + /// Rust 1.79.0 or newer. + const ASSERT_IS_SOME: () =3D { assert!(Self::IS_SOME) }; + + /// `true` if `Self` is an actual function type and not `()`. + /// + /// # Examples + /// + /// You can use `IS_SOME` to catch this at compile time: + /// + /// ```compile_fail + /// # use qemu_api::callbacks::FnCall; + /// fn call_it FnCall<(&'a str,), String>>(_f: &F, s: &str)= -> String { + /// const { assert!(F::IS_SOME) } + /// F::call((s,)) + /// } + /// + /// let s: String =3D call_it((), "hello world"); // does not compile + /// ``` + const IS_SOME: bool; + + /// `false` if `Self` is an actual function type, `true` if it is `()`. + fn is_none() -> bool { + !Self::IS_SOME + } + + /// `true` if `Self` is an actual function type, `false` if it is `()`. + fn is_some() -> bool { + Self::IS_SOME + } + /// Call the function with the arguments in args. fn call(a: Args) -> R; } =20 +/// `()` acts as a "null" callback. Using `()` and `function` is nicer +/// than `None` and `Some(function)`, because the compiler is unable to +/// infer the type of just `None`. Therefore, the trait itself acts as the +/// option type, with functions [`FnCall::is_some`] and [`FnCall::is_none`= ]. +unsafe impl FnCall for () { + const IS_SOME: bool =3D false; + + /// Call the function with the arguments in args. + fn call(_a: Args) -> R { + panic!("callback not specified") + } +} + macro_rules! impl_call { ($($args:ident,)* ) =3D> ( // SAFETY: because each function is treated as a separate type, @@ -103,6 +188,8 @@ unsafe impl FnCall<($($args,)*), R> fo= r F where F: 'static + Sync + Sized + Fn($($args, )*) -> R, { + const IS_SOME: bool =3D true; + #[inline(always)] fn call(a: ($($args,)*)) -> R { let _: () =3D Self::ASSERT_ZERO_SIZED; @@ -138,4 +225,14 @@ fn do_test_call<'a, F: FnCall<(&'a str,), String>>(_f:= &F) -> String { fn test_call() { assert_eq!(do_test_call(&str::to_owned), "hello world") } + + // The `_f` parameter is unused but it helps the compiler infer `F`. + fn do_test_is_some<'a, F: FnCall<(&'a str,), String>>(_f: &F) { + assert_eq!(F::is_some(), true); + } + + #[test] + fn test_is_some() { + do_test_is_some(&str::to_owned); + } } --=20 2.47.1