Views, slicing, and explicit copies

A tensor view is a borrowed descriptor, not another owner. Use views when an operation can consume shape/stride metadata directly; use duplicate() when a fresh physical allocation is required.

Read-only views

as_view() is an O(1) reborrow. It preserves static rank, and the dynamic TensorView family preserves dtype and layout metadata without a transfer. The typed forms are TypedTensorView<T, R> and TypedTensorViewMut<T, R>.

use tenferro_tensor::{Rank, TypedTensor};

let tensor = TypedTensor::<f64, Rank<2>>::from_vec_col_major([2, 3], vec![1.0; 6])?;
let view = tensor.as_view();
assert_eq!(view.shape(), &[2, 3]);
assert_eq!(view.strides(), &[1, 2]);
assert_eq!(view.get(&[1, 2]), Some(&1.0));

The view can be transformed without copying:

let transposed = view.transpose_view([1, 0])?;
let reversed = transposed.try_slice(&[
    tenferro_tensor::StridedSliceSpec::all(),
    tenferro_tensor::StridedSliceSpec::reverse(),
])?;
assert_eq!(reversed.shape(), &[3, 2]);

These transformations preserve the same root allocation. They do not make a host slice available for a backend buffer and they do not perform a transfer.

Mutable views and disjointness

as_view_mut() requires an exclusive borrow of the owner. Use one mutable view at a time unless the checked disjoint-view API proves that physical regions do not overlap. Mutation is visible through later views of the same owner:

use tenferro_tensor::{Rank, TypedTensor};

let mut tensor = TypedTensor::<f64, Rank<2>>::from_vec_col_major([2, 2], vec![0.0; 4])?;
{
    let mut view = tensor.as_view_mut();
    *view.get_mut(&[1, 0]).ok_or("missing element")? = 4.0;
}
assert_eq!(tensor.as_slice()?, &[0.0, 4.0, 0.0, 0.0]);

A mutable view is not Clone, and there is no public mutable owner projection. The borrow checker and descriptor validation are the alias-safety boundary.

Explicit duplication

Call duplicate() when the next API needs an owned compact tensor. The operation reads the view once and allocates a fresh owner; it never silently uploads, downloads, or canonicalizes a backend view.

use tenferro_tensor::{Rank, TypedTensor};

let tensor = TypedTensor::<f64, Rank<1>>::from_vec_col_major([3], vec![1.0, 2.0, 3.0])?;
let view = tensor.as_view();
let duplicate = view.duplicate()?;
assert_eq!(duplicate.as_slice()?, &[1.0, 2.0, 3.0]);
assert_ne!(view.as_slice()?.as_ptr(), duplicate.as_slice()?.as_ptr());

For a provider-resident view, use the provider’s explicit same-device materialization or download_to_host operation. A CPU backend that cannot transfer a borrowed/noncompact layout returns a typed unsupported error; call duplicate() first if compact host ownership is what the operation requires.

Prepared element access

Prepared access is the boundary between validation and traversal. A prepared contiguous read uses a typed slice. A prepared strided read initializes one checked cursor and then advances its offset/carry state. No inner loop repeats provider lookup, allocation, synchronization, descriptor resolution, or full layout validation.

This is why the following two operations have different names and contracts:

Operation Physical effect
transpose_view, try_slice, reshape, reinterpretation Descriptor-only view
duplicate Fresh same-placement allocation and copy
upload_tensor Fresh provider allocation and host-to-provider transfer
download_tensor Fresh host allocation and provider-to-host transfer
map/host guard, synchronize Existing-allocation visibility/order operation

See storage ownership for detached/scoped execution, AD/checkpoint retention, provider namespaces, and Apple shared endpoint rules.