From 8d8ae29ba55436128ca3598fe2d914df5de76b5a Mon Sep 17 00:00:00 2001 From: Michael Morgan Date: Mon, 1 Mar 2021 22:02:24 -0500 Subject: [PATCH] Add TryFrom<&'_ [A::Item]> for ArrayVec (#134) --- src/arrayvec.rs | 35 ++++++++++++++++++++++++++++++++++- src/tinyvec.rs | 17 ++++------------- tests/arrayvec.rs | 18 ++++++++++++++++++ 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/arrayvec.rs b/src/arrayvec.rs index 26f21d4..7fa4263 100644 --- a/src/arrayvec.rs +++ b/src/arrayvec.rs @@ -1,5 +1,5 @@ use super::*; -use core::convert::TryInto; +use core::convert::{TryInto, TryFrom}; #[cfg(feature = "serde")] use core::marker::PhantomData; @@ -1190,6 +1190,39 @@ impl From for ArrayVec { } } +/// The error type returned when a conversion from a slice to an [`ArrayVec`] fails. +#[derive(Debug, Copy, Clone)] +pub struct TryFromSliceError(()); + +impl TryFrom<&'_ [T]> for ArrayVec +where + T: Clone + Default, + A: Array, +{ + type Error = TryFromSliceError; + + #[inline] + #[must_use] + /// The output has a length equal to that of the slice, with the same capacity as `A`. + fn try_from(slice: &[T]) -> Result { + if slice.len() > A::CAPACITY { + Err(TryFromSliceError(())) + } else { + let mut arr = ArrayVec::new(); + // We do not use ArrayVec::extend_from_slice, because it looks like LLVM + // fails to deduplicate all the length-checking logic between the + // above if and the contents of that method, thus producing much + // slower code. Unlike many of the other optimizations in this + // crate, this one is worth keeping an eye on. I see no reason, for + // any element type, that these should produce different code. But + // they do. (rustc 1.51.0) + arr.set_len(slice.len()); + arr.as_mut_slice().clone_from_slice(slice); + Ok(arr) + } + } +} + impl FromIterator for ArrayVec { #[inline] #[must_use] diff --git a/src/tinyvec.rs b/src/tinyvec.rs index 94beca5..82af1ce 100644 --- a/src/tinyvec.rs +++ b/src/tinyvec.rs @@ -3,6 +3,7 @@ use super::*; use alloc::vec::{self, Vec}; +use core::convert::TryFrom; use tinyvec_macros::impl_mirrored; #[cfg(feature = "serde")] @@ -1114,20 +1115,10 @@ where #[inline] #[must_use] fn from(slice: &[T]) -> Self { - if slice.len() > A::CAPACITY { - TinyVec::Heap(slice.into()) - } else { - let mut arr = ArrayVec::new(); - // We do not use ArrayVec::extend_from_slice, because it looks like LLVM - // fails to deduplicate all the length-checking logic between the - // above if and the contents of that method, thus producing much - // slower code. Unlike many of the other optimizations in this - // crate, this one is worth keeping an eye on. I see no reason, for - // any element type, that these should produce different code. But - // they do. (rustc 1.51.0) - arr.set_len(slice.len()); - arr.as_mut_slice().clone_from_slice(slice); + if let Ok(arr) = ArrayVec::try_from(slice) { TinyVec::Inline(arr) + } else { + TinyVec::Heap(slice.into()) } } } diff --git a/tests/arrayvec.rs b/tests/arrayvec.rs index ce848e0..5181b60 100644 --- a/tests/arrayvec.rs +++ b/tests/arrayvec.rs @@ -445,3 +445,21 @@ fn ArrayVec_ser_de() { ], ); } + +#[test] +fn ArrayVec_try_from_slice() { + use std::convert::TryFrom; + + let nums = [1, 2, 3, 4]; + + let empty: Result, _> = ArrayVec::try_from(&nums[..0]); + assert!(empty.is_ok()); + assert_eq!(empty.unwrap().as_slice(), &[]); + + let fits: Result, _> = ArrayVec::try_from(&nums[..2]); + assert!(fits.is_ok()); + assert_eq!(fits.unwrap().as_slice(), &[1, 2]); + + let doesnt_fit: Result, _> = ArrayVec::try_from(&nums[..4]); + assert!(doesnt_fit.is_err()); +}