Repository: graphql-rust/juniper (language: rust)

Issue:
Does not GraphQLObject support [u8; N] slice?
I have a struct with field of [u8; 5], but seems juniper does not support this type of data:

```rust
#[derive(GraphQLObject)]
pub struct Test {
    demo: [u8; 5]
}
```

the trait `GraphQLValue<__S>` is not implemented for `[u8; 9]`


Discussion / hints:
It appears we do not support that style yet. I started a patch, will hopefully get time to polish it up by this weekend.
I have the field side of this done, just wrestling with some input object stuff

Fix patch:
diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md
index 5121175f3..dcbc294d2 100644
--- a/juniper/CHANGELOG.md
+++ b/juniper/CHANGELOG.md
@@ -2,6 +2,7 @@
 
 - Allow spreading interface fragments on unions and other interfaces ([#965](https://github.com/graphql-rust/juniper/pull/965), [#798](https://github.com/graphql-rust/juniper/issues/798))
 - Expose `GraphQLRequest` fields ([#750](https://github.com/graphql-rust/juniper/issues/750))
+- Support using Rust array as GraphQL list ([#966](https://github.com/graphql-rust/juniper/pull/966), [#918](https://github.com/graphql-rust/juniper/issues/918))
 
 # [[0.15.7] 2021-07-08](https://github.com/graphql-rust/juniper/releases/tag/juniper-v0.15.7)
 
diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs
index 86ba9e80a..07aed950b 100644
--- a/juniper/src/ast.rs
+++ b/juniper/src/ast.rs
@@ -19,13 +19,13 @@ pub enum Type<'a> {
     /// A nullable list type, e.g. `[String]`
     ///
     /// The list itself is what's nullable, the containing type might be non-null.
-    List(Box<Type<'a>>),
+    List(Box<Type<'a>>, Option<usize>),
     /// A non-null named type, e.g. `String!`
     NonNullNamed(Cow<'a, str>),
     /// A non-null list type, e.g. `[String]!`.
     ///
     /// The list itself is what's non-null, the containing type might be null.
-    NonNullList(Box<Type<'a>>),
+    NonNullList(Box<Type<'a>>, Option<usize>),
 }
 
 /// A JSON-like value that can be passed into the query execution, either
@@ -192,13 +192,13 @@ impl<'a> Type<'a> {
     pub fn innermost_name(&self) -> &str {
         match *self {
             Type::Named(ref n) | Type::NonNullNamed(ref n) => n,
-            Type::List(ref l) | Type::NonNullList(ref l) => l.innermost_name(),
+            Type::List(ref l, _) | Type::NonNullList(ref l, _) => l.innermost_name(),
         }
     }
 
     /// Determines if a type only can represent non-null values.
     pub fn is_non_null(&self) -> bool {
-        matches!(*self, Type::NonNullNamed(_) | Type::NonNullList(_))
+        matches!(*self, Type::NonNullNamed(_) | Type::NonNullList(..))
     }
 }
 
@@ -207,8 +207,8 @@ impl<'a> fmt::Display for Type<'a> {
         match *self {
             Type::Named(ref n) => write!(f, "{}", n),
             Type::NonNullNamed(ref n) => write!(f, "{}!", n),
-            Type::List(ref t) => write!(f, "[{}]", t),
-            Type::NonNullList(ref t) => write!(f, "[{}]!", t),
+            Type::List(ref t, _) => write!(f, "[{}]", t),
+            Type::NonNullList(ref t, _) => write!(f, "[{}]!", t),
         }
     }
 }
diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs
index f4b56c254..5e7e3182e 100644
--- a/juniper/src/executor/mod.rs
+++ b/juniper/src/executor/mod.rs
@@ -1235,9 +1235,10 @@ where
     pub fn build_list_type<T: GraphQLType<S> + ?Sized>(
         &mut self,
         info: &T::TypeInfo,
+        expected_size: Option<usize>,
     ) -> ListMeta<'r> {
         let of_type = self.get_type::<T>(info);
-        ListMeta::new(of_type)
+        ListMeta::new(of_type, expected_size)
     }
 
     /// Create a nullable meta type
diff --git a/juniper/src/parser/document.rs b/juniper/src/parser/document.rs
index 096b39910..23063ce38 100644
--- a/juniper/src/parser/document.rs
+++ b/juniper/src/parser/document.rs
@@ -520,7 +520,11 @@ pub fn parse_type<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Type<'a>> {
     {
         let inner_type = parse_type(parser)?;
         let Spanning { end: end_pos, .. } = parser.expect(&Token::BracketClose)?;
-        Spanning::start_end(&start_pos, &end_pos, Type::List(Box::new(inner_type.item)))
+        Spanning::start_end(
+            &start_pos,
+            &end_pos,
+            Type::List(Box::new(inner_type.item), None),
+        )
     } else {
         parser.expect_name()?.map(|s| Type::Named(Cow::Borrowed(s)))
     };
@@ -542,7 +546,7 @@ fn wrap_non_null<'a>(
 
     let wrapped = match inner.item {
         Type::Named(name) => Type::NonNullNamed(name),
-        Type::List(l) => Type::NonNullList(l),
+        Type::List(l, expected_size) => Type::NonNullList(l, expected_size),
         t => t,
     };
 
diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs
index 7681539a0..beb8e0ac7 100644
--- a/juniper/src/schema/meta.rs
+++ b/juniper/src/schema/meta.rs
@@ -55,6 +55,9 @@ pub struct ScalarMeta<'a, S> {
 pub struct ListMeta<'a> {
     #[doc(hidden)]
     pub of_type: Type<'a>,
+
+    #[doc(hidden)]
+    pub expected_size: Option<usize>,
 }
 
 /// Nullable type metadata
@@ -311,12 +314,15 @@ impl<'a, S> MetaType<'a, S> {
             | MetaType::InputObject(InputObjectMeta { ref name, .. }) => {
                 Type::NonNullNamed(name.clone())
             }
-            MetaType::List(ListMeta { ref of_type }) => {
-                Type::NonNullList(Box::new(of_type.clone()))
-            }
+            MetaType::List(ListMeta {
+                ref of_type,
+                expected_size,
+            }) => Type::NonNullList(Box::new(of_type.clone()), expected_size),
             MetaType::Nullable(NullableMeta { ref of_type }) => match *of_type {
                 Type::NonNullNamed(ref inner) => Type::Named(inner.clone()),
-                Type::NonNullList(ref inner) => Type::List(inner.clone()),
+                Type::NonNullList(ref inner, expected_size) => {
+                    Type::List(inner.clone(), expected_size)
+                }
                 ref t => t.clone(),
             },
             MetaType::Placeholder(PlaceholderMeta { ref of_type }) => of_type.clone(),
@@ -446,8 +452,11 @@ where
 
 impl<'a> ListMeta<'a> {
     /// Build a new list type by wrapping the specified type
-    pub fn new(of_type: Type<'a>) -> ListMeta<'a> {
-        ListMeta { of_type }
+    pub fn new(of_type: Type<'a>, expected_size: Option<usize>) -> Self {
+        Self {
+            of_type,
+            expected_size,
+        }
     }
 
     /// Wrap the list in a generic meta type
diff --git a/juniper/src/schema/model.rs b/juniper/src/schema/model.rs
index 09d4c85e0..b0516fe04 100644
--- a/juniper/src/schema/model.rs
+++ b/juniper/src/schema/model.rs
@@ -62,7 +62,7 @@ impl<'a, S> Context for SchemaType<'a, S> {}
 pub enum TypeType<'a, S: 'a> {
     Concrete(&'a MetaType<'a, S>),
     NonNull(Box<TypeType<'a, S>>),
-    List(Box<TypeType<'a, S>>),
+    List(Box<TypeType<'a, S>>, Option<usize>),
 }
 
 #[derive(Debug)]
@@ -271,7 +271,7 @@ impl<'a, S> SchemaType<'a, S> {
             Type::NonNullNamed(ref name) | Type::Named(ref name) => {
                 self.concrete_type_by_name(name)
             }
-            Type::List(ref inner) | Type::NonNullList(ref inner) => self.lookup_type(inner),
+            Type::List(ref inner, _) | Type::NonNullList(ref inner, _) => self.lookup_type(inner),
         }
     }
 
@@ -339,11 +339,13 @@ impl<'a, S> SchemaType<'a, S> {
             Type::NonNullNamed(ref n) => TypeType::NonNull(Box::new(
                 self.type_by_name(n).expect("Type not found in schema"),
             )),
-            Type::NonNullList(ref inner) => {
-                TypeType::NonNull(Box::new(TypeType::List(Box::new(self.make_type(inner)))))
-            }
+            Type::NonNullList(ref inner, expected_size) => TypeType::NonNull(Box::new(
+                TypeType::List(Box::new(self.make_type(inner)), expected_size),
+            )),
             Type::Named(ref n) => self.type_by_name(n).expect("Type not found in schema"),
-            Type::List(ref inner) => TypeType::List(Box::new(self.make_type(inner))),
+            Type::List(ref inner, expected_size) => {
+                TypeType::List(Box::new(self.make_type(inner)), expected_size)
+            }
         }
     }
 
@@ -423,9 +425,9 @@ impl<'a, S> SchemaType<'a, S> {
             | (&Named(ref super_name), &NonNullNamed(ref sub_name)) => {
                 self.is_named_subtype(sub_name, super_name)
             }
-            (&NonNullList(ref super_inner), &NonNullList(ref sub_inner))
-            | (&List(ref super_inner), &List(ref sub_inner))
-            | (&List(ref super_inner), &NonNullList(ref sub_inner)) => {
+            (&NonNullList(ref super_inner, _), &NonNullList(ref sub_inner, _))
+            | (&List(ref super_inner, _), &List(ref sub_inner, _))
+            | (&List(ref super_inner, _), &NonNullList(ref sub_inner, _)) => {
                 self.is_subtype(sub_inner, super_inner)
             }
             _ => false,
@@ -460,14 +462,14 @@ impl<'a, S> TypeType<'a, S> {
     pub fn innermost_concrete(&self) -> &'a MetaType<S> {
         match *self {
             TypeType::Concrete(t) => t,
-            TypeType::NonNull(ref n) | TypeType::List(ref n) => n.innermost_concrete(),
+            TypeType::NonNull(ref n) | TypeType::List(ref n, _) => n.innermost_concrete(),
         }
     }
 
     #[inline]
     pub fn list_contents(&self) -> Option<&TypeType<'a, S>> {
         match *self {
-            TypeType::List(ref n) => Some(n),
+            TypeType::List(ref n, _) => Some(n),
             TypeType::NonNull(ref n) => n.list_contents(),
             _ => None,
         }
@@ -550,7 +552,7 @@ impl<'a, S> fmt::Display for TypeType<'a, S> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
             TypeType::Concrete(t) => f.write_str(t.name().unwrap()),
-            TypeType::List(ref i) => write!(f, "[{}]", i),
+            TypeType::List(ref i, _) => write!(f, "[{}]", i),
             TypeType::NonNull(ref i) => write!(f, "{}!", i),
         }
     }
diff --git a/juniper/src/schema/schema.rs b/juniper/src/schema/schema.rs
index f9a5a6f90..a877afe78 100644
--- a/juniper/src/schema/schema.rs
+++ b/juniper/src/schema/schema.rs
@@ -201,7 +201,7 @@ where
     fn kind(&self) -> TypeKind {
         match *self {
             TypeType::Concrete(t) => t.type_kind(),
-            TypeType::List(_) => TypeKind::List,
+            TypeType::List(..) => TypeKind::List,
             TypeType::NonNull(_) => TypeKind::NonNull,
         }
     }
@@ -224,7 +224,7 @@ where
     fn of_type(&self) -> Option<&Box<TypeType<S>>> {
         match *self {
             TypeType::Concrete(_) => None,
-            TypeType::List(ref l) | TypeType::NonNull(ref l) => Some(l),
+            TypeType::List(ref l, _) | TypeType::NonNull(ref l) => Some(l),
         }
     }
 
diff --git a/juniper/src/schema/translate/graphql_parser.rs b/juniper/src/schema/translate/graphql_parser.rs
index e642faa0c..3c8ac75f7 100644
--- a/juniper/src/schema/translate/graphql_parser.rs
+++ b/juniper/src/schema/translate/graphql_parser.rs
@@ -140,13 +140,13 @@ impl GraphQLParserTranslator {
     {
         match input {
             Type::Named(x) => ExternalType::NamedType(From::from(x.as_ref())),
-            Type::List(x) => {
+            Type::List(x, _) => {
                 ExternalType::ListType(GraphQLParserTranslator::translate_type(x).into())
             }
             Type::NonNullNamed(x) => {
                 ExternalType::NonNullType(Box::new(ExternalType::NamedType(From::from(x.as_ref()))))
             }
-            Type::NonNullList(x) => ExternalType::NonNullType(Box::new(ExternalType::ListType(
+            Type::NonNullList(x, _) => ExternalType::NonNullType(Box::new(ExternalType::ListType(
                 Box::new(GraphQLParserTranslator::translate_type(x)),
             ))),
         }
diff --git a/juniper/src/types/containers.rs b/juniper/src/types/containers.rs
index cb3d2ad0b..e4977fb0b 100644
--- a/juniper/src/types/containers.rs
+++ b/juniper/src/types/containers.rs
@@ -1,3 +1,8 @@
+use std::{
+    mem::{self, MaybeUninit},
+    ptr,
+};
+
 use crate::{
     ast::{FromInputValue, InputValue, Selection, ToInputValue},
     executor::{ExecutionResult, Executor, Registry},
@@ -80,7 +85,7 @@ where
     T: FromInputValue<S>,
     S: ScalarValue,
 {
-    fn from_input_value(v: &InputValue<S>) -> Option<Option<T>> {
+    fn from_input_value(v: &InputValue<S>) -> Option<Self> {
         match v {
             &InputValue::Null => Some(None),
             v => v.convert().map(Some),
@@ -114,7 +119,7 @@ where
     where
         S: 'r,
     {
-        registry.build_list_type::<T>(info).into_meta()
+        registry.build_list_type::<T>(info, None).into_meta()
     }
 }
 
@@ -163,17 +168,11 @@ where
     T: FromInputValue<S>,
     S: ScalarValue,
 {
-    fn from_input_value(v: &InputValue<S>) -> Option<Vec<T>>
-where {
+    fn from_input_value(v: &InputValue<S>) -> Option<Self> {
         match *v {
             InputValue::List(ref ls) => {
                 let v: Vec<_> = ls.iter().filter_map(|i| i.item.convert()).collect();
-
-                if v.len() == ls.len() {
-                    Some(v)
-                } else {
-                    None
-                }
+                (v.len() == ls.len()).then(|| v)
             }
             ref other => other.convert().map(|e| vec![e]),
         }
@@ -203,7 +202,7 @@ where
     where
         S: 'r,
     {
-        registry.build_list_type::<T>(info).into_meta()
+        registry.build_list_type::<T>(info, None).into_meta()
     }
 }
 
@@ -257,6 +256,179 @@ where
     }
 }
 
+impl<S, T, const N: usize> GraphQLType<S> for [T; N]
+where
+    S: ScalarValue,
+    T: GraphQLType<S>,
+{
+    fn name(_: &Self::TypeInfo) -> Option<&'static str> {
+        None
+    }
+
+    fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S>
+    where
+        S: 'r,
+    {
+        registry.build_list_type::<T>(info, Some(N)).into_meta()
+    }
+}
+
+impl<S, T, const N: usize> GraphQLValue<S> for [T; N]
+where
+    S: ScalarValue,
+    T: GraphQLValue<S>,
+{
+    type Context = T::Context;
+    type TypeInfo = T::TypeInfo;
+
+    fn type_name(&self, _: &Self::TypeInfo) -> Option<&'static str> {
+        None
+    }
+
+    fn resolve(
+        &self,
+        info: &Self::TypeInfo,
+        _: Option<&[Selection<S>]>,
+        executor: &Executor<Self::Context, S>,
+    ) -> ExecutionResult<S> {
+        resolve_into_list(executor, info, self.iter())
+    }
+}
+
+impl<S, T, const N: usize> GraphQLValueAsync<S> for [T; N]
+where
+    T: GraphQLValueAsync<S>,
+    T::TypeInfo: Sync,
+    T::Context: Sync,
+    S: ScalarValue + Send + Sync,
+{
+    fn resolve_async<'a>(
+        &'a self,
+        info: &'a Self::TypeInfo,
+        _: Option<&'a [Selection<S>]>,
+        executor: &'a Executor<Self::Context, S>,
+    ) -> crate::BoxFuture<'a, ExecutionResult<S>> {
+        let f = resolve_into_list_async(executor, info, self.iter());
+        Box::pin(f)
+    }
+}
+
+impl<T, S, const N: usize> FromInputValue<S> for [T; N]
+where
+    T: FromInputValue<S>,
+    S: ScalarValue,
+{
+    fn from_input_value(v: &InputValue<S>) -> Option<Self> {
+        struct PartiallyInitializedArray<T, const N: usize> {
+            arr: [MaybeUninit<T>; N],
+            init_len: usize,
+            no_drop: bool,
+        }
+
+        impl<T, const N: usize> Drop for PartiallyInitializedArray<T, N> {
+            fn drop(&mut self) {
+                if self.no_drop {
+                    return;
+                }
+                // Dropping a `MaybeUninit` does nothing, thus we need to drop
+                // the initialized elements manually, otherwise we may introduce
+                // a memory/resource leak if `T: Drop`.
+                for elem in &mut self.arr[0..self.init_len] {
+                    // SAFETY: This is safe, because `self.init_len` represents
+                    //         exactly the number of initialized elements.
+                    unsafe {
+                        ptr::drop_in_place(elem.as_mut_ptr());
+                    }
+                }
+            }
+        }
+
+        match *v {
+            InputValue::List(ref ls) => {
+                // SAFETY: The reason we're using a wrapper struct implementing
+                //         `Drop` here is to be panic safe:
+                //         `T: FromInputValue<S>` implementation is not
+                //         controlled by us, so calling `i.item.convert()` below
+                //         may cause a panic when our array is initialized only
+                //         partially. In such situation we need to drop already
+                //         initialized values to avoid possible memory/resource
+                //         leaks if `T: Drop`.
+                let mut out = PartiallyInitializedArray::<T, N> {
+                    // SAFETY: The `.assume_init()` here is safe, because the
+                    //         type we are claiming to have initialized here is
+                    //         a bunch of `MaybeUninit`s, which do not require
+                    //         any initialization.
+                    arr: unsafe { MaybeUninit::uninit().assume_init() },
+                    init_len: 0,
+                    no_drop: false,
+                };
+
+                let mut items = ls.iter().filter_map(|i| i.item.convert());
+                for elem in &mut out.arr[..] {
+                    if let Some(i) = items.next() {
+                        *elem = MaybeUninit::new(i);
+                        out.init_len += 1;
+                    } else {
+                        // There is not enough `items` to fill the array.
+                        return None;
+                    }
+                }
+                if items.next().is_some() {
+                    // There is too much `items` to fit into the array.
+                    return None;
+                }
+
+                // Do not drop collected `items`, because we're going to return
+                // them.
+                out.no_drop = true;
+
+                // TODO: Use `mem::transmute` instead of `mem::transmute_copy`
+                //       below, once it's allowed for const generics:
+                //       https://github.com/rust-lang/rust/issues/61956
+                // SAFETY: `mem::transmute_copy` is safe here, because we have
+                //         exactly `N` initialized `items`.
+                //         Also, despite `mem::transmute_copy` copies the value,
+                //         we won't have a double-free when `T: Drop` here,
+                //         because original array elements are `MaybeUninit`, so
+                //         do nothing on `Drop`.
+                Some(unsafe { mem::transmute_copy::<_, Self>(&out.arr) })
+            }
+            ref other => {
+                other.convert().and_then(|e: T| {
+                    // TODO: Use `mem::transmute` instead of
+                    //       `mem::transmute_copy` below, once it's allowed for
+                    //       const generics:
+                    //       https://github.com/rust-lang/rust/issues/61956
+                    if N == 1 {
+                        // SAFETY: `mem::transmute_copy` is safe here, because
+                        //         we check `N` to be `1`.
+                        //         Also, despite `mem::transmute_copy` copies
+                        //         the value, we won't have a double-free when
+                        //         `T: Drop` here, because original `e: T` value
+                        //         is wrapped into `mem::ManuallyDrop`, so does
+                        //         nothing on `Drop`.
+                        Some(unsafe {
+                            mem::transmute_copy::<_, Self>(&[mem::ManuallyDrop::new(e)])
+                        })
+                    } else {
+                        None
+                    }
+                })
+            }
+        }
+    }
+}
+
+impl<T, S, const N: usize> ToInputValue<S> for [T; N]
+where
+    T: ToInputValue<S>,
+    S: ScalarValue,
+{
+    fn to_input_value(&self) -> InputValue<S> {
+        InputValue::list(self.iter().map(T::to_input_value).collect())
+    }
+}
+
 fn resolve_into_list<'t, S, T, I>(
     executor: &Executor<T::Context, S>,
     info: &T::TypeInfo,
diff --git a/juniper/src/types/marker.rs b/juniper/src/types/marker.rs
index b04ee327e..7c07f4aae 100644
--- a/juniper/src/types/marker.rs
+++ b/juniper/src/types/marker.rs
@@ -253,6 +253,17 @@ where
     }
 }
 
+impl<S, T, const N: usize> IsOutputType<S> for [T; N]
+where
+    T: IsOutputType<S>,
+    S: ScalarValue,
+{
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
+}
+
 impl<'a, S> IsOutputType<S> for str where S: ScalarValue {}
 
 /// Marker trait for types which can be used as input types.
@@ -335,4 +346,15 @@ where
     }
 }
 
+impl<S, T, const N: usize> IsInputType<S> for [T; N]
+where
+    T: IsInputType<S>,
+    S: ScalarValue,
+{
+    #[inline]
+    fn mark() {
+        T::mark()
+    }
+}
+
 impl<'a, S> IsInputType<S> for str where S: ScalarValue {}
diff --git a/juniper/src/types/utilities.rs b/juniper/src/types/utilities.rs
index 74271e77d..1d1ec5fd6 100644
--- a/juniper/src/types/utilities.rs
+++ b/juniper/src/types/utilities.rs
@@ -24,11 +24,25 @@ where
                 is_valid_literal_value(schema, inner, arg_value)
             }
         }
-        TypeType::List(ref inner) => match *arg_value {
-            InputValue::List(ref items) => items
-                .iter()
-                .all(|i| is_valid_literal_value(schema, inner, &i.item)),
-            ref v => is_valid_literal_value(schema, inner, v),
+        TypeType::List(ref inner, expected_size) => match *arg_value {
+            InputValue::List(ref items) => {
+                if let Some(expected) = expected_size {
+                    if items.len() != expected {
+                        return false;
+                    }
+                }
+                items
+                    .iter()
+                    .all(|i| is_valid_literal_value(schema, inner, &i.item))
+            }
+            ref v => {
+                if let Some(expected) = expected_size {
+                    if expected != 1 {
+                        return false;
+                    }
+                }
+                is_valid_literal_value(schema, inner, v)
+            }
         },
         TypeType::Concrete(t) => {
             // Even though InputValue::String can be parsed into an enum, they
diff --git a/juniper/src/validation/input_value.rs b/juniper/src/validation/input_value.rs
index d2e26a852..a24eaac52 100644
--- a/juniper/src/validation/input_value.rs
+++ b/juniper/src/validation/input_value.rs
@@ -108,13 +108,27 @@ where
             }
         }
 
-        TypeType::List(ref inner) => {
+        TypeType::List(ref inner, expected_size) => {
             if value.is_null() {
                 return errors;
             }
 
             match value.to_list_value() {
                 Some(l) => {
+                    if let Some(expected) = expected_size {
+                        if l.len() != expected {
+                            errors.push(unification_error(
+                                var_name,
+                                var_pos,
+                                &path,
+                                &format!(
+                                    "Expected list of {} elements, found {} elements",
+                                    expected,
+                                    l.len()
+                                ),
+                            ));
+                        }
+                    }
                     for (i, v) in l.iter().enumerate() {
                         errors.append(&mut unify_value(
                             var_name,
diff --git a/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs b/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs
index ce42cbd88..cf44b5521 100644
--- a/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs
+++ b/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs
@@ -529,8 +529,14 @@ impl<'a, S: Debug> OverlappingFieldsCanBeMerged<'a, S> {
 
     fn is_type_conflict(&self, ctx: &ValidatorContext<'a, S>, t1: &Type, t2: &Type) -> bool {
         match (t1, t2) {
-            (&Type::List(ref inner1), &Type::List(ref inner2))
-            | (&Type::NonNullList(ref inner1), &Type::NonNullList(ref inner2)) => {
+            (&Type::List(ref inner1, expected_size1), &Type::List(ref inner2, expected_size2))
+            | (
+                &Type::NonNullList(ref inner1, expected_size1),
+                &Type::NonNullList(ref inner2, expected_size2),
+            ) => {
+                if expected_size1 != expected_size2 {
+                    return false;
+                }
                 self.is_type_conflict(ctx, inner1, inner2)
             }
             (&Type::NonNullNamed(ref n1), &Type::NonNullNamed(ref n2))
diff --git a/juniper/src/validation/rules/variables_in_allowed_position.rs b/juniper/src/validation/rules/variables_in_allowed_position.rs
index 9e7e3fc31..6391ca680 100644
--- a/juniper/src/validation/rules/variables_in_allowed_position.rs
+++ b/juniper/src/validation/rules/variables_in_allowed_position.rs
@@ -54,7 +54,9 @@ impl<'a, S: Debug> VariableInAllowedPosition<'a, S> {
                     .find(|&&&(ref n, _)| n.item == var_name.item)
                 {
                     let expected_type = match (&var_def.default_value, &var_def.var_type.item) {
-                        (&Some(_), &Type::List(ref inner)) => Type::NonNullList(inner.clone()),
+                        (&Some(_), &Type::List(ref inner, expected_size)) => {
+                            Type::NonNullList(inner.clone(), expected_size)
+                        }
                         (&Some(_), &Type::Named(ref inner)) => {
                             Type::NonNullNamed(Cow::Borrowed(inner))
                         }
diff --git a/juniper/src/validation/visitor.rs b/juniper/src/validation/visitor.rs
index cd1d50505..21c31112b 100644
--- a/juniper/src/validation/visitor.rs
+++ b/juniper/src/validation/visitor.rs
@@ -336,7 +336,7 @@ fn visit_input_value<'a, S, V>(
         }
         InputValue::List(ref ls) => {
             let inner_type = ctx.current_input_type_literal().and_then(|t| match *t {
-                Type::List(ref inner) | Type::NonNullList(ref inner) => {
+                Type::List(ref inner, _) | Type::NonNullList(ref inner, _) => {
                     Some(inner.as_ref().clone())
                 }
                 _ => None,
diff --git a/juniper/src/value/mod.rs b/juniper/src/value/mod.rs
index ab8a762b1..14e1989cd 100644
--- a/juniper/src/value/mod.rs
+++ b/juniper/src/value/mod.rs
@@ -164,8 +164,12 @@ impl<S: ScalarValue> Value<S> {
     /// Maps the [`ScalarValue`] type of this [`Value`] into the specified one.
     pub fn map_scalar_value<Into: ScalarValue>(self) -> Value<Into> {
         if TypeId::of::<Into>() == TypeId::of::<S>() {
-            // This is totally safe, because we're transmuting the value into itself,
-            // so no invariants may change and we're just satisfying the type checker.
+            // SAFETY: This is safe, because we're transmuting the value into
+            //         itself, so no invariants may change and we're just
+            //         satisfying the type checker.
+            //         As `mem::transmute_copy` creates a copy of data, we need
+            //         `mem::ManuallyDrop` here to omit double-free when
+            //         `S: Drop`.
             let val = mem::ManuallyDrop::new(self);
             unsafe { mem::transmute_copy(&*val) }
         } else {
diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs
index 31c173ca3..63ac9c5b2 100644
--- a/juniper_codegen/src/util/mod.rs
+++ b/juniper_codegen/src/util/mod.rs
@@ -1731,7 +1731,7 @@ impl GraphQLTypeDefiniton {
                         // TODO: investigate the unwraps here, they seem dangerous!
                         match obj.get(#field_name) {
                             #from_input_default
-                            Some(ref v) => ::juniper::FromInputValue::from_input_value(v).unwrap(),
+                            Some(ref v) => ::juniper::FromInputValue::from_input_value(v)?,
                             None => ::juniper::FromInputValue::<#scalar>::from_implicit_null(),
                         }
                     },
@@ -1849,15 +1849,10 @@ impl GraphQLTypeDefiniton {
             {
                 fn from_input_value(value: &::juniper::InputValue<#scalar>) -> Option<Self>
                 {
-                    if let Some(obj) = value.to_object_value() {
-                        let item = #ty {
-                            #( #from_inputs )*
-                        };
-                        Some(item)
-                    }
-                    else {
-                        None
-                    }
+                    let obj = value.to_object_value()?;
+                    Some(#ty {
+                        #( #from_inputs )*
+                    })
                 }
             }
 
diff --git a/juniper_graphql_ws/src/server_message.rs b/juniper_graphql_ws/src/server_message.rs
index bad7091a5..252681e10 100644
--- a/juniper_graphql_ws/src/server_message.rs
+++ b/juniper_graphql_ws/src/server_message.rs
@@ -1,6 +1,7 @@
+use std::{any::Any, fmt, marker::PhantomPinned, mem};
+
 use juniper::{ExecutionError, GraphQLError, ScalarValue, Value};
 use serde::{Serialize, Serializer};
-use std::{any::Any, fmt, marker::PhantomPinned};
 
 /// The payload for errors that are not associated with a GraphQL operation.
 #[derive(Debug, Serialize, PartialEq)]
@@ -45,7 +46,7 @@ impl ErrorPayload {
     ) -> Self {
         Self {
             _execution_params: Some(execution_params),
-            error: std::mem::transmute(error),
+            error: mem::transmute(error),
             _marker: PhantomPinned,
         }
     }


Test patch:
diff --git a/integration_tests/codegen_fail/fail/interface/argument_wrong_default_array.rs b/integration_tests/codegen_fail/fail/interface/argument_wrong_default_array.rs
new file mode 100644
index 000000000..85156a3eb
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/argument_wrong_default_array.rs
@@ -0,0 +1,23 @@
+use juniper::{graphql_interface, GraphQLObject};
+
+#[derive(GraphQLObject)]
+#[graphql(impl = CharacterValue)]
+pub struct ObjA {
+    test: String,
+}
+
+#[graphql_interface]
+impl Character for ObjA {}
+
+#[graphql_interface(for = ObjA)]
+trait Character {
+    fn wrong(
+        &self,
+        #[graphql(default = [true, false, false])]
+        input: [bool; 2],
+    ) -> bool {
+        input[0]
+    }
+}
+
+fn main() {}
diff --git a/integration_tests/codegen_fail/fail/interface/argument_wrong_default_array.stderr b/integration_tests/codegen_fail/fail/interface/argument_wrong_default_array.stderr
new file mode 100644
index 000000000..1b7e861c7
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/interface/argument_wrong_default_array.stderr
@@ -0,0 +1,12 @@
+error[E0277]: the trait bound `[bool; 2]: From<[bool; 3]>` is not satisfied
+  --> $DIR/argument_wrong_default_array.rs:12:1
+   |
+12 | #[graphql_interface(for = ObjA)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<[bool; 3]>` is not implemented for `[bool; 2]`
+   |
+   = help: the following implementations were found:
+             <&'a [ascii::ascii_char::AsciiChar] as From<&'a ascii::ascii_str::AsciiStr>>
+             <&'a [u8] as From<&'a ascii::ascii_str::AsciiStr>>
+             <&'a mut [ascii::ascii_char::AsciiChar] as From<&'a mut ascii::ascii_str::AsciiStr>>
+   = note: required because of the requirements on the impl of `Into<[bool; 2]>` for `[bool; 3]`
+   = note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/codegen_fail/fail/object/impl_argument_wrong_default_array.rs b/integration_tests/codegen_fail/fail/object/impl_argument_wrong_default_array.rs
new file mode 100644
index 000000000..740400c4d
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/object/impl_argument_wrong_default_array.rs
@@ -0,0 +1,11 @@
+struct Object;
+
+#[juniper::graphql_object]
+impl Object {
+    #[graphql(arguments(input(default = [true, false, false])))]
+    fn wrong(input: [bool; 2]) -> bool {
+        input[0]
+    }
+}
+
+fn main() {}
diff --git a/integration_tests/codegen_fail/fail/object/impl_argument_wrong_default_array.stderr b/integration_tests/codegen_fail/fail/object/impl_argument_wrong_default_array.stderr
new file mode 100644
index 000000000..eb85ff256
--- /dev/null
+++ b/integration_tests/codegen_fail/fail/object/impl_argument_wrong_default_array.stderr
@@ -0,0 +1,7 @@
+error[E0308]: mismatched types
+ --> $DIR/impl_argument_wrong_default_array.rs:3:1
+  |
+3 | #[juniper::graphql_object]
+  | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an array with a fixed size of 2 elements, found one with 3 elements
+  |
+  = note: this error originates in the attribute macro `juniper::graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/integration_tests/juniper_tests/src/array.rs b/integration_tests/juniper_tests/src/array.rs
new file mode 100644
index 000000000..43cbd8ea4
--- /dev/null
+++ b/integration_tests/juniper_tests/src/array.rs
@@ -0,0 +1,257 @@
+use juniper::{
+    graphql_object, graphql_value, EmptyMutation, EmptySubscription, GraphQLInputObject, RootNode,
+    Variables,
+};
+
+mod as_output_field {
+    use super::*;
+
+    struct Query;
+
+    #[graphql_object]
+    impl Query {
+        fn roll() -> [bool; 3] {
+            [true, false, true]
+        }
+    }
+
+    #[tokio::test]
+    async fn works() {
+        let query = r#"
+            query Query {
+                roll
+            }
+        "#;
+
+        let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
+        let (res, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
+            .await
+            .unwrap();
+
+        assert_eq!(errors.len(), 0);
+        assert_eq!(res, graphql_value!({"roll": [true, false, true]}));
+    }
+}
+
+mod as_input_field {
+    use super::*;
+
+    #[derive(GraphQLInputObject)]
+    struct Input {
+        two: [bool; 2],
+    }
+
+    #[derive(GraphQLInputObject)]
+    struct InputSingle {
+        one: [bool; 1],
+    }
+
+    struct Query;
+
+    #[graphql_object]
+    impl Query {
+        fn first(input: InputSingle) -> bool {
+            input.one[0]
+        }
+
+        fn second(input: Input) -> bool {
+            input.two[1]
+        }
+    }
+
+    #[tokio::test]
+    async fn works() {
+        let query = r#"
+            query Query {
+                second(input: { two: [true, false] })
+            }
+        "#;
+
+        let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
+        let (res, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
+            .await
+            .unwrap();
+
+        assert_eq!(errors.len(), 0);
+        assert_eq!(res, graphql_value!({"second": false}));
+    }
+
+    #[tokio::test]
+    async fn fails_on_incorrect_count() {
+        let query = r#"
+            query Query {
+                second(input: { two: [true, true, false] })
+            }
+        "#;
+
+        let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
+        let res = juniper::execute(query, None, &schema, &Variables::new(), &()).await;
+
+        assert!(res.is_err());
+        assert!(res
+            .unwrap_err()
+            .to_string()
+            .contains(r#"Invalid value for argument "input", expected type "Input!""#));
+    }
+
+    #[tokio::test]
+    async fn cannot_coerce_from_raw_value_if_multiple() {
+        let query = r#"
+            query Query {
+                second(input: { two: true })
+            }
+        "#;
+
+        let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
+        let res = juniper::execute(query, None, &schema, &Variables::new(), &()).await;
+
+        assert!(res.is_err());
+        assert!(res
+            .unwrap_err()
+            .to_string()
+            .contains(r#"Invalid value for argument "input", expected type "Input!""#));
+    }
+
+    #[tokio::test]
+    async fn can_coerce_from_raw_value_if_single() {
+        let query = r#"
+            query Query {
+                first(input: { one: true })
+            }
+        "#;
+
+        let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
+        let (res, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
+            .await
+            .unwrap();
+
+        assert_eq!(errors.len(), 0);
+        assert_eq!(res, graphql_value!({"first": true}));
+    }
+}
+
+mod as_input_argument {
+    use super::*;
+
+    struct Query;
+
+    #[graphql_object]
+    impl Query {
+        fn second(input: [bool; 2]) -> bool {
+            input[1]
+        }
+
+        fn first(input: [bool; 1]) -> bool {
+            input[0]
+        }
+
+        #[graphql(arguments(input(default = [true, false, false])))]
+        fn third(input: [bool; 3]) -> bool {
+            input[2]
+        }
+    }
+
+    #[tokio::test]
+    async fn works() {
+        let query = r#"
+            query Query {
+                second(input: [false, true])
+            }
+        "#;
+
+        let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
+        let (res, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
+            .await
+            .unwrap();
+
+        assert_eq!(errors.len(), 0);
+        assert_eq!(res, graphql_value!({"second": true}));
+    }
+
+    #[tokio::test]
+    async fn fails_on_incorrect_count() {
+        let query = r#"
+            query Query {
+                second(input: [true, true, false])
+            }
+        "#;
+
+        let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
+        let res = juniper::execute(query, None, &schema, &Variables::new(), &()).await;
+
+        assert!(res.is_err());
+        assert!(res
+            .unwrap_err()
+            .to_string()
+            .contains(r#"Invalid value for argument "input", expected type "[Boolean!]!""#));
+    }
+
+    #[tokio::test]
+    async fn cannot_coerce_from_raw_value_if_multiple() {
+        let query = r#"
+            query Query {
+                second(input: true)
+            }
+        "#;
+
+        let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
+        let res = juniper::execute(query, None, &schema, &Variables::new(), &()).await;
+
+        assert!(res.is_err());
+        assert!(res
+            .unwrap_err()
+            .to_string()
+            .contains(r#"Invalid value for argument "input", expected type "[Boolean!]!""#));
+    }
+
+    #[tokio::test]
+    async fn can_coerce_from_raw_value_if_single() {
+        let query = r#"
+            query Query {
+                first(input: true)
+            }
+        "#;
+
+        let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
+        let (res, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
+            .await
+            .unwrap();
+
+        assert_eq!(errors.len(), 0);
+        assert_eq!(res, graphql_value!({"first": true}));
+    }
+
+    #[tokio::test]
+    async fn picks_default() {
+        let query = r#"
+            query Query {
+                third
+            }
+        "#;
+
+        let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
+        let (res, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
+            .await
+            .unwrap();
+
+        assert_eq!(errors.len(), 0);
+        assert_eq!(res, graphql_value!({"third": false}));
+    }
+
+    #[tokio::test]
+    async fn picks_specified_over_default() {
+        let query = r#"
+            query Query {
+                third(input: [false, false, true])
+            }
+        "#;
+
+        let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
+        let (res, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
+            .await
+            .unwrap();
+
+        assert_eq!(errors.len(), 0);
+        assert_eq!(res, graphql_value!({"third": true}));
+    }
+}
diff --git a/integration_tests/juniper_tests/src/lib.rs b/integration_tests/juniper_tests/src/lib.rs
index 43a922da9..5fe40091d 100644
--- a/integration_tests/juniper_tests/src/lib.rs
+++ b/integration_tests/juniper_tests/src/lib.rs
@@ -1,6 +1,8 @@
 #[cfg(test)]
 mod arc_fields;
 #[cfg(test)]
+mod array;
+#[cfg(test)]
 mod codegen;
 #[cfg(test)]
 mod custom_scalar;
diff --git a/juniper/src/validation/test_harness.rs b/juniper/src/validation/test_harness.rs
index a2af2194b..7bd75f7b0 100644
--- a/juniper/src/validation/test_harness.rs
+++ b/juniper/src/validation/test_harness.rs
@@ -1,3 +1,5 @@
+use std::mem;
+
 use crate::{
     ast::{FromInputValue, InputValue},
     executor::Registry,
@@ -862,12 +864,10 @@ where
 
     let doc =
         parse_document_source(q, &root.schema).expect(&format!("Parse error on input {:#?}", q));
-    let mut ctx = ValidatorContext::new(unsafe { ::std::mem::transmute(&root.schema) }, &doc);
+    let mut ctx = ValidatorContext::new(unsafe { mem::transmute(&root.schema) }, &doc);
 
     let mut mv = MultiVisitorNil.with(factory());
-    visit(&mut mv, &mut ctx, unsafe {
-        ::std::mem::transmute(doc.as_slice())
-    });
+    visit(&mut mv, &mut ctx, unsafe { mem::transmute(doc.as_slice()) });
 
     ctx.into_errors()
 }