diff --git a/src/dimension/broadcast.rs b/src/dimension/broadcast.rs
index dc1513f04..6470dd58b 100644
--- a/src/dimension/broadcast.rs
+++ b/src/dimension/broadcast.rs
@@ -34,6 +34,57 @@ where
     Ok(out)
 }
 
+/// Return new stride when trying to grow `from` into shape `to`
+///
+/// Broadcasting works by returning a "fake stride" where elements
+/// to repeat are in axes with 0 stride, so that several indexes point
+/// to the same element.
+///
+/// **Note:** Cannot be used for mutable iterators, since repeating
+/// elements would create aliasing pointers.
+pub(crate) fn upcast<D: Dimension, E: Dimension>(to: &D, from: &E, stride: &E) -> Option<D> {
+    // Make sure the product of non-zero axis lengths does not exceed
+    // `isize::MAX`. This is the only safety check we need to perform
+    // because all the other constraints of `ArrayBase` are guaranteed
+    // to be met since we're starting from a valid `ArrayBase`.
+    let _ = size_of_shape_checked(to).ok()?;
+
+    let mut new_stride = to.clone();
+    // begin at the back (the least significant dimension)
+    // size of the axis has to either agree or `from` has to be 1
+    if to.ndim() < from.ndim() {
+        return None;
+    }
+
+    {
+        let mut new_stride_iter = new_stride.slice_mut().iter_mut().rev();
+        for ((er, es), dr) in from
+            .slice()
+            .iter()
+            .rev()
+            .zip(stride.slice().iter().rev())
+            .zip(new_stride_iter.by_ref())
+        {
+            /* update strides */
+            if *dr == *er {
+                /* keep stride */
+                *dr = *es;
+            } else if *er == 1 {
+                /* dead dimension, zero stride */
+                *dr = 0
+            } else {
+                return None;
+            }
+        }
+
+        /* set remaining strides to zero */
+        for dr in new_stride_iter {
+            *dr = 0;
+        }
+    }
+    Some(new_stride)
+}
+
 pub trait DimMax<Other: Dimension> {
     /// The resulting dimension type after broadcasting.
     type Output: Dimension;
diff --git a/src/impl_methods.rs b/src/impl_methods.rs
index 115cd2d71..ad9e11685 100644
--- a/src/impl_methods.rs
+++ b/src/impl_methods.rs
@@ -22,7 +22,7 @@ use crate::dimension::{
     abs_index, axes_of, do_slice, merge_axes, move_min_stride_axis_to_last,
     offset_from_low_addr_ptr_to_logical_ptr, size_of_shape_checked, stride_offset, Axes,
 };
-use crate::dimension::broadcast::co_broadcast;
+use crate::dimension::broadcast::{co_broadcast, upcast};
 use crate::dimension::reshape_dim;
 use crate::error::{self, ErrorKind, ShapeError, from_kind};
 use crate::math_cell::MathCell;
@@ -2036,56 +2036,6 @@ where
         E: IntoDimension,
         S: Data,
     {
-        /// Return new stride when trying to grow `from` into shape `to`
-        ///
-        /// Broadcasting works by returning a "fake stride" where elements
-        /// to repeat are in axes with 0 stride, so that several indexes point
-        /// to the same element.
-        ///
-        /// **Note:** Cannot be used for mutable iterators, since repeating
-        /// elements would create aliasing pointers.
-        fn upcast<D: Dimension, E: Dimension>(to: &D, from: &E, stride: &E) -> Option<D> {
-            // Make sure the product of non-zero axis lengths does not exceed
-            // `isize::MAX`. This is the only safety check we need to perform
-            // because all the other constraints of `ArrayBase` are guaranteed
-            // to be met since we're starting from a valid `ArrayBase`.
-            let _ = size_of_shape_checked(to).ok()?;
-
-            let mut new_stride = to.clone();
-            // begin at the back (the least significant dimension)
-            // size of the axis has to either agree or `from` has to be 1
-            if to.ndim() < from.ndim() {
-                return None;
-            }
-
-            {
-                let mut new_stride_iter = new_stride.slice_mut().iter_mut().rev();
-                for ((er, es), dr) in from
-                    .slice()
-                    .iter()
-                    .rev()
-                    .zip(stride.slice().iter().rev())
-                    .zip(new_stride_iter.by_ref())
-                {
-                    /* update strides */
-                    if *dr == *er {
-                        /* keep stride */
-                        *dr = *es;
-                    } else if *er == 1 {
-                        /* dead dimension, zero stride */
-                        *dr = 0
-                    } else {
-                        return None;
-                    }
-                }
-
-                /* set remaining strides to zero */
-                for dr in new_stride_iter {
-                    *dr = 0;
-                }
-            }
-            Some(new_stride)
-        }
         let dim = dim.into_dimension();
 
         // Note: zero strides are safe precisely because we return an read-only view
diff --git a/src/impl_views/methods.rs b/src/impl_views/methods.rs
new file mode 100644
index 000000000..a34247165
--- /dev/null
+++ b/src/impl_views/methods.rs
@@ -0,0 +1,34 @@
+// Copyright 2014-2016 bluss and ndarray developers.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use crate::imp_prelude::*;
+use crate::dimension::IntoDimension;
+use crate::dimension::broadcast::upcast;
+
+impl<'a, A, D> ArrayView<'a, A, D>
+where
+    D: Dimension,
+{
+    /// Broadcasts an `ArrayView`. See [`ArrayBase::broadcast`].
+    ///
+    /// This is a specialized version of [`ArrayBase::broadcast`] that transfers
+    /// the view's lifetime to the output.
+    pub fn broadcast_ref<E>(&self, dim: E) -> Option<ArrayView<'a, A, E::Dim>>
+    where
+        E: IntoDimension,
+    {
+        let dim = dim.into_dimension();
+
+        // Note: zero strides are safe precisely because we return an read-only view
+        let broadcast_strides = match upcast(&dim, &self.dim, &self.strides) {
+            Some(st) => st,
+            None => return None,
+        };
+        unsafe { Some(ArrayView::new(self.ptr, dim, broadcast_strides)) }
+    }
+}
diff --git a/src/impl_views/mod.rs b/src/impl_views/mod.rs
index 487cc3cb2..fda58242a 100644
--- a/src/impl_views/mod.rs
+++ b/src/impl_views/mod.rs
@@ -1,6 +1,7 @@
 mod constructors;
 mod conversions;
 mod indexing;
+mod methods;
 mod splitting;
 
 pub use constructors::*;