2
0
Fork 0
mirror of https://github.com/bevyengine/bevy synced 2025-01-03 16:58:56 +00:00

Optimized FunctionMap

This commit is contained in:
Gino Valente 2024-09-04 16:21:13 -07:00
parent b3b6671d9c
commit 5923cfa0ec
5 changed files with 309 additions and 190 deletions

View file

@ -12,7 +12,6 @@ use crate::{
};
use alloc::{borrow::Cow, boxed::Box, sync::Arc};
use bevy_reflect_derive::impl_type_path;
use bevy_utils::HashMap;
use core::fmt::{Debug, Formatter};
#[cfg(not(feature = "std"))]
@ -90,7 +89,7 @@ impl<'env> DynamicFunction<'env> {
/// [function overloading]: Self::with_overload
pub fn new<F: for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>(
func: F,
info: impl TryInto<FunctionInfoType, Error: Debug>,
info: impl TryInto<FunctionInfoType<'static>, Error: Debug>,
) -> Self {
let info = info.try_into().unwrap();
@ -102,19 +101,14 @@ impl<'env> DynamicFunction<'env> {
FunctionInfoType::Overloaded(_) => None,
},
function_map: match info {
FunctionInfoType::Standard(info) => FunctionMap {
functions: vec![func],
indices: HashMap::from([(ArgumentSignature::from(&info), 0)]),
info: FunctionInfoType::Standard(info),
},
FunctionInfoType::Overloaded(infos) => FunctionMap {
functions: vec![func],
indices: infos
FunctionInfoType::Standard(info) => FunctionMap::Single(func, info.into_owned()),
FunctionInfoType::Overloaded(infos) => {
let indices = infos
.iter()
.map(|info| (ArgumentSignature::from(info), 0))
.collect(),
info: FunctionInfoType::Overloaded(infos),
},
.collect();
FunctionMap::Overloaded(vec![func], infos.into_owned(), indices)
}
},
}
}
@ -302,10 +296,10 @@ impl<'env> DynamicFunction<'env> {
///
/// The function itself may also return any errors it needs to.
pub fn call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> {
let expected_arg_count = self.function_map.info.arg_count();
let expected_arg_count = self.function_map.info().arg_count();
let received_arg_count = args.len();
if matches!(self.function_map.info, FunctionInfoType::Standard(_))
if matches!(self.function_map.info(), FunctionInfoType::Standard(_))
&& expected_arg_count != received_arg_count
{
Err(FunctionError::ArgCountMismatch {
@ -319,8 +313,8 @@ impl<'env> DynamicFunction<'env> {
}
/// Returns the function info.
pub fn info(&self) -> &FunctionInfoType {
&self.function_map.info
pub fn info(&self) -> FunctionInfoType {
self.function_map.info()
}
/// The name of the function.
@ -347,8 +341,8 @@ impl Function for DynamicFunction<'static> {
self.name.as_ref()
}
fn info(&self) -> &FunctionInfoType {
&self.function_map.info
fn info(&self) -> FunctionInfoType {
self.function_map.info()
}
fn reflect_call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> {

View file

@ -8,7 +8,6 @@ use crate::func::{
args::ArgList, function_map::FunctionMap, signature::ArgumentSignature, DynamicFunction,
FunctionError, FunctionInfoType, FunctionOverloadError, FunctionResult, IntoFunctionMut,
};
use bevy_utils::HashMap;
/// A [`Box`] containing a callback to a reflected function.
type BoxFnMut<'env> = Box<dyn for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>;
@ -93,7 +92,7 @@ impl<'env> DynamicFunctionMut<'env> {
/// [function overloading]: Self::with_overload
pub fn new<F: for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>(
func: F,
info: impl TryInto<FunctionInfoType, Error: Debug>,
info: impl TryInto<FunctionInfoType<'static>, Error: Debug>,
) -> Self {
let info = info.try_into().unwrap();
@ -105,19 +104,14 @@ impl<'env> DynamicFunctionMut<'env> {
FunctionInfoType::Overloaded(_) => None,
},
function_map: match info {
FunctionInfoType::Standard(info) => FunctionMap {
functions: vec![func],
indices: HashMap::from([(ArgumentSignature::from(&info), 0)]),
info: FunctionInfoType::Standard(info),
},
FunctionInfoType::Overloaded(infos) => FunctionMap {
functions: vec![func],
indices: infos
FunctionInfoType::Standard(info) => FunctionMap::Single(func, info.into_owned()),
FunctionInfoType::Overloaded(infos) => {
let indices = infos
.iter()
.map(|info| (ArgumentSignature::from(info), 0))
.collect(),
info: FunctionInfoType::Overloaded(infos),
},
.collect();
FunctionMap::Overloaded(vec![func], infos.into_owned(), indices)
}
},
}
}
@ -272,10 +266,10 @@ impl<'env> DynamicFunctionMut<'env> {
///
/// [`call_once`]: DynamicFunctionMut::call_once
pub fn call<'a>(&mut self, args: ArgList<'a>) -> FunctionResult<'a> {
let expected_arg_count = self.function_map.info.arg_count();
let expected_arg_count = self.function_map.info().arg_count();
let received_arg_count = args.len();
if matches!(self.function_map.info, FunctionInfoType::Standard(_))
if matches!(self.function_map.info(), FunctionInfoType::Standard(_))
&& expected_arg_count != received_arg_count
{
Err(FunctionError::ArgCountMismatch {
@ -321,8 +315,8 @@ impl<'env> DynamicFunctionMut<'env> {
}
/// Returns the function info.
pub fn info(&self) -> &FunctionInfoType {
&self.function_map.info
pub fn info(&self) -> FunctionInfoType {
self.function_map.info()
}
/// The name of the function.
@ -378,15 +372,13 @@ impl<'env> From<DynamicFunction<'env>> for DynamicFunctionMut<'env> {
fn from(function: DynamicFunction<'env>) -> Self {
Self {
name: function.name,
function_map: FunctionMap {
info: function.function_map.info,
indices: function.function_map.indices,
functions: function
.function_map
.functions
.into_iter()
.map(arc_to_box)
.collect(),
function_map: match function.function_map {
FunctionMap::Single(func, info) => FunctionMap::Single(arc_to_box(func), info),
FunctionMap::Overloaded(funcs, infos, indices) => FunctionMap::Overloaded(
funcs.into_iter().map(arc_to_box).collect(),
infos,
indices,
),
},
}
}

View file

@ -53,7 +53,7 @@ pub trait Function: PartialReflect + Debug {
}
/// The [`FunctionInfoType`] for this function.
fn info(&self) -> &FunctionInfoType;
fn info(&self) -> FunctionInfoType;
/// Call this function with the given arguments.
fn reflect_call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a>;

View file

@ -1,16 +1,16 @@
use crate::func::signature::ArgumentSignature;
use crate::func::{ArgList, FunctionError, FunctionInfoType, FunctionOverloadError};
use crate::func::{ArgList, FunctionError, FunctionInfo, FunctionInfoType, FunctionOverloadError};
use alloc::borrow::Cow;
use bevy_utils::HashMap;
/// A helper type for storing a mapping of overloaded functions
/// along with the corresponding [function information].
///
/// [function information]: FunctionInfoType
/// [function information]: FunctionInfo
#[derive(Clone, Debug)]
pub(super) struct FunctionMap<F> {
pub info: FunctionInfoType,
pub functions: Vec<F>,
pub indices: HashMap<ArgumentSignature, usize>,
pub(super) enum FunctionMap<F> {
Single(F, FunctionInfo),
Overloaded(Vec<F>, Vec<FunctionInfo>, HashMap<ArgumentSignature, usize>),
}
impl<F> FunctionMap<F> {
@ -21,17 +21,18 @@ impl<F> FunctionMap<F> {
///
/// If no overload matches the provided arguments, an error will be returned.
pub fn get(&self, args: &ArgList) -> Result<&F, FunctionError> {
if self.functions.len() == 1 {
Ok(&self.functions[0])
} else {
let signature = ArgumentSignature::from(args);
self.indices
.get(&signature)
.map(|index| &self.functions[*index])
.ok_or_else(|| FunctionError::NoOverload {
expected: self.indices.keys().cloned().collect(),
received: signature,
})
match self {
Self::Single(function, _) => Ok(function),
Self::Overloaded(functions, _, indices) => {
let signature = ArgumentSignature::from(args);
indices
.get(&signature)
.map(|index| &functions[*index])
.ok_or_else(|| FunctionError::NoOverload {
expected: indices.keys().cloned().collect(),
received: signature,
})
}
}
}
@ -42,17 +43,26 @@ impl<F> FunctionMap<F> {
///
/// If no overload matches the provided arguments, an error will be returned.
pub fn get_mut(&mut self, args: &ArgList) -> Result<&mut F, FunctionError> {
if self.functions.len() == 1 {
Ok(&mut self.functions[0])
} else {
let signature = ArgumentSignature::from(args);
self.indices
.get(&signature)
.map(|index| &mut self.functions[*index])
.ok_or_else(|| FunctionError::NoOverload {
expected: self.indices.keys().cloned().collect(),
received: signature,
})
match self {
Self::Single(function, _) => Ok(function),
Self::Overloaded(functions, _, indices) => {
let signature = ArgumentSignature::from(args);
indices
.get(&signature)
.map(|index| &mut functions[*index])
.ok_or_else(|| FunctionError::NoOverload {
expected: indices.keys().cloned().collect(),
received: signature,
})
}
}
}
/// Returns the function information contained in the map.
pub fn info(&self) -> FunctionInfoType {
match self {
Self::Single(_, info) => FunctionInfoType::Standard(Cow::Borrowed(info)),
Self::Overloaded(_, info, _) => FunctionInfoType::Overloaded(Cow::Borrowed(info)),
}
}
@ -60,42 +70,97 @@ impl<F> FunctionMap<F> {
///
/// If the other map contains any functions with the same signature as this one,
/// an error will be returned along with the original, unchanged map.
pub fn merge(mut self, other: Self) -> Result<Self, (Box<Self>, FunctionOverloadError)> {
// === Function Map === //
let mut other_indices = HashMap::new();
pub fn merge(self, other: Self) -> Result<Self, (Box<Self>, FunctionOverloadError)> {
match (self, other) {
(Self::Single(self_func, self_info), Self::Single(other_func, other_info)) => {
let self_sig = ArgumentSignature::from(&self_info);
let other_sig = ArgumentSignature::from(&other_info);
if self_sig == other_sig {
return Err((
Box::new(Self::Single(self_func, self_info)),
FunctionOverloadError {
signature: self_sig,
},
));
}
for (sig, index) in other.indices {
if self.indices.contains_key(&sig) {
return Err((Box::new(self), FunctionOverloadError { signature: sig }));
Ok(Self::Overloaded(
vec![self_func, other_func],
vec![self_info, other_info],
HashMap::from([(self_sig, 0), (other_sig, 1)]),
))
}
(
Self::Single(self_func, self_info),
Self::Overloaded(mut other_funcs, mut other_infos, mut other_indices),
) => {
let self_sig = ArgumentSignature::from(&self_info);
if other_indices.contains_key(&self_sig) {
return Err((
Box::new(Self::Single(self_func, self_info)),
FunctionOverloadError {
signature: self_sig,
},
));
}
other_indices.insert(sig, self.functions.len() + index);
for index in other_indices.values_mut() {
*index += 1;
}
other_funcs.insert(0, self_func);
other_infos.insert(0, self_info);
other_indices.insert(self_sig, 0);
Ok(Self::Overloaded(other_funcs, other_infos, other_indices))
}
(
Self::Overloaded(mut self_funcs, mut self_infos, mut self_indices),
Self::Single(other_func, other_info),
) => {
let other_sig = ArgumentSignature::from(&other_info);
if self_indices.contains_key(&other_sig) {
return Err((
Box::new(Self::Overloaded(self_funcs, self_infos, self_indices)),
FunctionOverloadError {
signature: other_sig,
},
));
}
let index = self_funcs.len();
self_funcs.push(other_func);
self_infos.push(other_info);
self_indices.insert(other_sig, index);
Ok(Self::Overloaded(self_funcs, self_infos, self_indices))
}
(
Self::Overloaded(mut self_funcs, mut self_infos, mut self_indices),
Self::Overloaded(mut other_funcs, mut other_infos, other_indices),
) => {
let mut new_indices = HashMap::new();
for (sig, index) in other_indices {
if self_indices.contains_key(&sig) {
return Err((
Box::new(Self::Overloaded(self_funcs, self_infos, self_indices)),
FunctionOverloadError { signature: sig },
));
}
new_indices.insert(sig, self_funcs.len() + index);
}
self_indices.extend(new_indices);
self_funcs.append(&mut other_funcs);
// The index map and `FunctionInfo` list should always be in sync,
// so we can simply append the new infos to the existing ones.
self_infos.append(&mut other_infos);
Ok(Self::Overloaded(self_funcs, self_infos, self_indices))
}
}
// === Function Info === //
let mut other_infos = Vec::new();
for info in other.info.into_iter() {
let sig = ArgumentSignature::from(&info);
if self.indices.contains_key(&sig) {
return Err((Box::new(self), FunctionOverloadError { signature: sig }));
}
other_infos.push(info);
}
// === Update === //
self.indices.extend(other_indices);
self.functions.extend(other.functions);
self.info = match self.info {
FunctionInfoType::Standard(info) => {
FunctionInfoType::Overloaded(std::iter::once(info).chain(other_infos).collect())
}
FunctionInfoType::Overloaded(infos) => FunctionInfoType::Overloaded(
IntoIterator::into_iter(infos).chain(other_infos).collect(),
),
};
Ok(self)
}
}
@ -106,100 +171,166 @@ mod tests {
use crate::Type;
#[test]
fn should_merge_function_maps() {
let map_a = FunctionMap {
info: FunctionInfoType::Overloaded(Box::new([
FunctionInfo::anonymous().with_arg::<i8>("arg0"),
FunctionInfo::anonymous().with_arg::<i16>("arg0"),
FunctionInfo::anonymous().with_arg::<i32>("arg0"),
])),
functions: vec!['a', 'b', 'c'],
indices: HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<i32>()]), 2),
]),
};
fn should_merge_single_into_single() {
let map_a = FunctionMap::Single('a', FunctionInfo::anonymous().with_arg::<i8>("arg0"));
let map_b = FunctionMap::Single('b', FunctionInfo::anonymous().with_arg::<u8>("arg0"));
let map_b = FunctionMap {
info: FunctionInfoType::Overloaded(Box::new([
let FunctionMap::Overloaded(functions, infos, indices) = map_a.merge(map_b).unwrap() else {
panic!("expected overloaded function");
};
assert_eq!(functions, vec!['a', 'b']);
assert_eq!(infos.len(), 2);
assert_eq!(
indices,
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 1),
])
);
}
#[test]
fn should_merge_single_into_overloaded() {
let map_a = FunctionMap::Single('a', FunctionInfo::anonymous().with_arg::<i8>("arg0"));
let map_b = FunctionMap::Overloaded(
vec!['b', 'c'],
vec![
FunctionInfo::anonymous().with_arg::<u8>("arg0"),
FunctionInfo::anonymous().with_arg::<u16>("arg0"),
FunctionInfo::anonymous().with_arg::<u32>("arg0"),
])),
functions: vec!['d', 'e', 'f'],
indices: HashMap::from([
],
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<u8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<u16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<u32>()]), 2),
]),
);
let FunctionMap::Overloaded(functions, infos, indices) = map_a.merge(map_b).unwrap() else {
panic!("expected overloaded function");
};
let map = map_a.merge(map_b).unwrap();
assert_eq!(map.functions, vec!['a', 'b', 'c', 'd', 'e', 'f']);
assert_eq!(functions, vec!['a', 'b', 'c']);
assert_eq!(infos.len(), 3);
assert_eq!(
map.indices,
indices,
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 1),
(ArgumentSignature::from_iter([Type::of::<u16>()]), 2),
])
);
}
#[test]
fn should_merge_overloaed_into_single() {
let map_a = FunctionMap::Overloaded(
vec!['a', 'b'],
vec![
FunctionInfo::anonymous().with_arg::<i8>("arg0"),
FunctionInfo::anonymous().with_arg::<i16>("arg0"),
],
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<i32>()]), 2),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 3),
(ArgumentSignature::from_iter([Type::of::<u16>()]), 4),
(ArgumentSignature::from_iter([Type::of::<u32>()]), 5),
]),
);
let map_b = FunctionMap::Single('c', FunctionInfo::anonymous().with_arg::<u8>("arg0"));
let FunctionMap::Overloaded(functions, infos, indices) = map_a.merge(map_b).unwrap() else {
panic!("expected overloaded function");
};
assert_eq!(functions, vec!['a', 'b', 'c']);
assert_eq!(infos.len(), 3);
assert_eq!(
indices,
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 2),
])
);
}
#[test]
fn should_merge_overloaded_into_overloaded() {
let map_a = FunctionMap::Overloaded(
vec!['a', 'b'],
vec![
FunctionInfo::anonymous().with_arg::<i8>("arg0"),
FunctionInfo::anonymous().with_arg::<i16>("arg0"),
],
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
]),
);
let map_b = FunctionMap::Overloaded(
vec!['c', 'd'],
vec![
FunctionInfo::anonymous().with_arg::<u8>("arg0"),
FunctionInfo::anonymous().with_arg::<u16>("arg0"),
],
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<u8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<u16>()]), 1),
]),
);
let FunctionMap::Overloaded(functions, infos, indices) = map_a.merge(map_b).unwrap() else {
panic!("expected overloaded function");
};
assert_eq!(functions, vec!['a', 'b', 'c', 'd']);
assert_eq!(infos.len(), 4);
assert_eq!(
indices,
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<u8>()]), 2),
(ArgumentSignature::from_iter([Type::of::<u16>()]), 3),
])
);
}
#[test]
fn should_return_error_on_duplicate_signature() {
let map_a = FunctionMap {
info: FunctionInfoType::Overloaded(Box::new([
FunctionInfo::anonymous().with_arg::<i8>("arg0"),
FunctionInfo::anonymous().with_arg::<i16>("arg0"),
FunctionInfo::anonymous().with_arg::<i32>("arg0"),
])),
functions: vec!['a', 'b', 'c'],
indices: HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<i32>()]), 2),
]),
};
let map_b = FunctionMap {
info: FunctionInfoType::Overloaded(Box::new([
let map_a = FunctionMap::Single(
'a',
FunctionInfo::anonymous()
.with_arg::<i8>("arg0")
.with_arg::<i16>("arg1"),
);
let map_b = FunctionMap::Overloaded(
vec!['b', 'c'],
vec![
FunctionInfo::anonymous().with_arg::<u8>("arg0"),
FunctionInfo::anonymous().with_arg::<i16>("arg0"),
FunctionInfo::anonymous().with_arg::<u32>("arg0"),
])),
functions: vec!['d', 'e', 'f'],
indices: HashMap::from([
(ArgumentSignature::from_iter([Type::of::<u8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<u32>()]), 2),
FunctionInfo::anonymous().with_arg::<u16>("arg1"),
],
HashMap::from([
(
ArgumentSignature::from_iter([Type::of::<u8>(), Type::of::<u16>()]),
0,
),
(
ArgumentSignature::from_iter([Type::of::<i8>(), Type::of::<i16>()]),
1,
),
]),
};
let Err((map_a, error)) = map_a.merge(map_b) else {
panic!("expected an error");
};
assert_eq!(
error,
FunctionOverloadError {
signature: ArgumentSignature::from_iter([Type::of::<i16>()])
}
);
// Assert that the original map remains unchanged:
assert_eq!(map_a.functions, vec!['a', 'b', 'c']);
let (map, error) = map_a.merge(map_b).unwrap_err();
assert_eq!(
map_a.indices,
HashMap::from([
(ArgumentSignature::from_iter([Type::of::<i8>()]), 0),
(ArgumentSignature::from_iter([Type::of::<i16>()]), 1),
(ArgumentSignature::from_iter([Type::of::<i32>()]), 2),
])
error.signature,
ArgumentSignature::from_iter([Type::of::<i8>(), Type::of::<i16>()])
);
// Assert the original map remains unchanged:
let FunctionMap::Single(function, info) = *map else {
panic!("expected single function");
};
assert_eq!(function, 'a');
assert_eq!(
ArgumentSignature::from(info),
ArgumentSignature::from_iter([Type::of::<i8>(), Type::of::<i16>()])
);
}
}

View file

@ -17,49 +17,51 @@ use crate::{
/// A wrapper around [`FunctionInfo`] used to represent either a standard function
/// or an overloaded function.
#[derive(Debug, Clone)]
pub enum FunctionInfoType {
pub enum FunctionInfoType<'a> {
/// A standard function with a single set of arguments.
///
/// This includes generic functions with a single set of monomorphized arguments.
Standard(FunctionInfo),
Standard(Cow<'a, FunctionInfo>),
/// An overloaded function with multiple sets of arguments.
///
/// This includes generic functions with multiple sets of monomorphized arguments,
/// as well as functions with a variable number of arguments (i.e. "variadic functions").
Overloaded(Box<[FunctionInfo]>),
Overloaded(Cow<'a, [FunctionInfo]>),
}
impl From<FunctionInfo> for FunctionInfoType {
impl From<FunctionInfo> for FunctionInfoType<'_> {
fn from(info: FunctionInfo) -> Self {
FunctionInfoType::Standard(info)
FunctionInfoType::Standard(Cow::Owned(info))
}
}
impl TryFrom<Vec<FunctionInfo>> for FunctionInfoType {
impl TryFrom<Vec<FunctionInfo>> for FunctionInfoType<'_> {
type Error = MissingFunctionInfoError;
fn try_from(mut infos: Vec<FunctionInfo>) -> Result<Self, Self::Error> {
match infos.len() {
0 => Err(MissingFunctionInfoError),
1 => Ok(Self::Standard(infos.pop().unwrap())),
_ => Ok(Self::Overloaded(infos.into_boxed_slice())),
1 => Ok(Self::Standard(Cow::Owned(infos.pop().unwrap()))),
_ => Ok(Self::Overloaded(Cow::Owned(infos))),
}
}
}
impl IntoIterator for FunctionInfoType {
impl IntoIterator for FunctionInfoType<'_> {
type Item = FunctionInfo;
type IntoIter = vec::IntoIter<FunctionInfo>;
fn into_iter(self) -> Self::IntoIter {
// Allow `.into_owned()` so that we can create a `std::vec::IntoIter`
#[allow(clippy::unnecessary_to_owned)]
match self {
FunctionInfoType::Standard(info) => vec![info].into_iter(),
FunctionInfoType::Overloaded(infos) => infos.into_vec().into_iter(),
FunctionInfoType::Standard(info) => vec![info.into_owned()].into_iter(),
FunctionInfoType::Overloaded(infos) => infos.into_owned().into_iter(),
}
}
}
impl FunctionInfoType {
impl FunctionInfoType<'_> {
pub fn arg_count(&self) -> usize {
match self {
Self::Standard(info) => info.arg_count(),