Repository: typst/typst
File: crates/typst/src/foundations/array.rs (Rust)

Code before:
                    value
                ]
                .into_value())
            })
            .collect()
    }

    /// Zips the array with other arrays.
    ///
    /// Returns an array of arrays, where the `i`th inner array contains all the
    /// `i`th elements from each original array.
    ///
    /// If the arrays to be zipped have different lengths, they are zipped up to
    /// the last element of the shortest array and all remaining elements are
    /// ignored.
    ///
    /// This function is variadic, meaning that you can zip multiple arrays
    /// together at once: `{(1, 2).zip(("A", "B"), (10, 20))}` yields
    /// `{((1, "A", 10), (2, "B", 20))}`.
    #[func]
    pub fn zip(
        self,
        /// The real arguments (the other arguments are just for the docs, this
        /// function is a bit involved, so we parse the arguments manually).
        args: &mut Args,
        #[external]
        #[named]
        #[default(false)]
        exact: bool,
        /// The arrays to zip with.
        #[external]
        #[variadic]
        others: Vec<Array>,
    ) -> SourceResult<Array> {
        let exact: bool = args.named("exact")?.unwrap_or_default();

        let remaining = args.remaining();

        // Fast path for one array.
        if remaining == 0 {
            return Ok(self.into_iter().map(|item| array![item].into_value()).collect());
        }

        // Fast path for just two arrays.
        if remaining == 1 {
            let Spanned { v: other, span: other_span } =
                args.expect::<Spanned<Array>>("others")?;
            if exact && self.len() != other.len() {
                bail!(
                    other_span,
                    "Second argument has different length ({}) from first argument ({})",

Review comment (suggestion):
I don't think this needs `#[external]` and manual parsing. That's only necessary for the other argument because it has special handling.

Code after:
                ]
                .into_value())
            })
            .collect()
    }

    /// Zips the array with other arrays.
    ///
    /// Returns an array of arrays, where the `i`th inner array contains all the
    /// `i`th elements from each original array.
    ///
    /// If the arrays to be zipped have different lengths, they are zipped up to
    /// the last element of the shortest array and all remaining elements are
    /// ignored.
    ///
    /// This function is variadic, meaning that you can zip multiple arrays
    /// together at once: `{(1, 2).zip(("A", "B"), (10, 20))}` yields
    /// `{((1, "A", 10), (2, "B", 20))}`.
    #[func]
    pub fn zip(
        self,
        /// The real arguments (the `others` arguments are just for the docs, this
        /// function is a bit involved, so we parse the positional arguments manually).
        args: &mut Args,
        /// Whether all arrays have to have the same length.
        /// For example, `(1, 2).zip((1, 2, 3), exact: true)` produces an error.
        #[named]
        #[default(false)]
        exact: bool,
        /// The arrays to zip with.
        #[external]
        #[variadic]
        others: Vec<Array>,
    ) -> SourceResult<Array> {
        let remaining = args.remaining();

        // Fast path for one array.
        if remaining == 0 {
            return Ok(self.into_iter().map(|item| array![item].into_value()).collect());
        }

        // Fast path for just two arrays.
        if remaining == 1 {
            let Spanned { v: other, span: other_span } =
                args.expect::<Spanned<Array>>("others")?;
            if exact && self.len() != other.len() {
                bail!(
                    other_span,
                    "second array has different length ({}) from first array ({})",
                    other.len(),
                    self.len()