Rust in Everything
基于 burn 0.22.1

检查与报错

主线位置:算子主线第一步的第一行。Tensor::matmul 的方法体从 check!(TensorCheck::matmul(&self, &other)) 开始,形状、秩、设备不对就在这里 panic,调用不会进入 Dispatch。本页讲这些检查怎么写、报错长什么样、哪些错误这一层拦不住、哪些错误以 Result 的形式交给用户。

flowchart TB
  callop["Tensor 方法"] --> chk["check! TensorCheck"]
  chk -->|Failed| fmt["format 拼报错"]
  fmt --> pnc["panic 在 burn-tensor"]
  chk -->|Ok| kop["K op 外提"]
  kop --> disp["Dispatch 生成的 match"]
  disp -->|后端不对| p2["panic wrong backend"]
  disp --> lazy["fusion 攒着执行"]
  lazy -->|执行出错| err["ExecutionError"]
  err --> rd["try_into_data 的 Err"]

本页实验 crate tensor-checks(模板的 feature 去掉了 autotune-checks):一个 match 按命令行参数跑一个用例,每个用例单独起一个进程,所以每段输出都是一次完整的 panic。平台 M2 Max、macOS 27、rustc 1.99.0,没有特别说明的都是 debug 构建。

TensorCheck:攒齐了再 panic

TensorCheck 是一个两态的枚举,文档注释先把立场说清楚:

/// # Notes
///
/// Failing tensor checks will always result in a panic.
/// As mentioned in [The Rust Programming Language book](https://doc.rust-lang.org/book/ch09-03-to-panic-or-not-to-panic.html),
/// when there is no way to recover, panic should be used instead of a result.
///
/// Most users will unwrap the results anyway, which will worsen the clarity of the code. Almost
/// all checks highlight programming errors, which means invalid programs that should be fixed.
/// ...
pub(crate) enum TensorCheck {
    Ok,
    Failed(FailedTensorCheck),
}

每个检查函数从 Self::Ok 开始,发现一个问题就 register 一条 TensorError,状态变成 Failed,错误列表继续往后追加;检查函数之间可以链式调用,例如逐元素二元运算的 binary_ops_ew 是「先查设备,再查形状」:

    pub(crate) fn binary_ops_ew<const D: usize, K: BasicOps>(
        ops: &str,
        lhs: &Tensor<D, K>,
        rhs: &Tensor<D, K>,
    ) -> Self {
        Self::Ok
            .binary_ops_device(ops, &lhs.device(), &rhs.device())
            .binary_ops_ew_shape::<D>(ops, &lhs.shape(), &rhs.shape())
    }

最后由 check! 宏决定要不要 panic:

pub(crate) mod macros {
    /// We use a macro for all checks, since the panic message file and line number will match the
    /// function that does the check instead of a generic error.rs crate private unrelated file
    /// and line number.
    macro_rules! check {
        ($check:expr) => {
            if let TensorCheck::Failed(check) = $check {
                core::panic!("{}", check.format());
            }
        };
    }
    pub(crate) use check;
}

宏里没有 cfg(debug_assertions),检查在 release 下照样跑。注释说用宏是为了让 panic 的位置落在「做检查的那个函数」上,也就是 numeric.rs:842(Tensor::matmul 的第一行),而不是某个公共的报错文件。但它落不到用户自己的代码行上:这些公开方法没有 #[track_caller]。FailedTensorCheck::format 把所有错误编号拼成一段:

    pub(crate) fn format(self) -> String {
        self.errors.into_iter().enumerate().fold(
            format!(
                "=== Tensor Operation Error ===\n  Operation: '{}'\n  Reason:",
                self.ops
            ),
            |accum, (number, error)| accum + error.format(number + 1).as_str(),
        ) + "\n"
    }

真实的报错

形状 [2, 3] 乘 [2, 3],内维不等:

let a = Tensor::<2>::ones([2, 3], &flex);
let b = Tensor::<2>::ones([2, 3], &flex);
let _ = a.matmul(b);

thread 'main' (3206393) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/numeric.rs:842:9:
=== Tensor Operation Error ===
  Operation: 'Matmul'
  Reason:
    1. The inner dimension of matmul should be the same, but got 3 and 2. Lhs shape Shape { dims: [2, 3] }, rhs shape Shape { dims: [2, 3] }. 

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

TensorCheck::matmul 按顺序查设备、秩、内维、批维广播。秩不够和批维不能广播各是一条:


thread 'main' (3206436) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/numeric.rs:842:9:
=== Tensor Operation Error ===
  Operation: 'Matmul'
  Reason:
    1. Matmul requires tensors with at least 2 dimensions, but got 1 dimension(s). 

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

thread 'main' (3206440) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/numeric.rs:842:9:
=== Tensor Operation Error ===
  Operation: 'Matmul'
  Reason:
    1. Tensors are not broadcastable along batch dimension 0: 2 and 3. Lhs shape Shape { dims: [2, 2, 3] }, rhs shape Shape { dims: [3, 3, 4] }. 

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

逐元素加法的两个维度都对不上时,两条错误一起列出来,这就是「攒齐了再 panic」的好处:


thread 'main' (3206444) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/numeric.rs:40:9:
=== Tensor Operation Error ===
  Operation: 'Add'
  Reason:
    1. The provided tensors have incompatible shapes. Incompatible size at dimension '0' => '2 != 3', which can't be broadcasted. Lhs tensor shape Shape { dims: [2, 3] }, Rhs tensor shape Shape { dims: [3, 2] }. 
    2. The provided tensors have incompatible shapes. Incompatible size at dimension '1' => '3 != 2', which can't be broadcasted. Lhs tensor shape Shape { dims: [2, 3] }, Rhs tensor shape Shape { dims: [3, 2] }. 

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

reshape 到元素数不同的形状、from_data 的数据秩和类型上的秩不同、维度下标越界:


thread 'main' (3206449) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/check.rs:1394:13:
=== Tensor Operation Error ===
  Operation: 'Reshape'
  Reason:
    1. The given shape doesn't have the same number of elements as the current shape. Current shape: [2, 3], target shape: [4, 2]. 

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

thread 'main' (3206465) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/base.rs:2347:9:
=== Tensor Operation Error ===
  Operation: 'From Data'
  Reason:
    1. Given dimensions differ from the tensor rank. Tensor rank: '2', given dimensions: '[3]'. 

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

thread 'main' (3206467) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/numeric.rs:419:19:
=== Tensor Operation Error ===
  Operation: 'Sum Dim'
  Reason:
    1. BoundsError: dimension 2 out of bounds: 0..2 

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

三条的 panic 位置各不相同:reshape 的错误来自 cubecl-zspace 的 MetadataError::Invalid,由 unwrap_shape_reshape 转成 TensorCheck 再 panic,所以位置是 check.rs:1394;维度下标由 unwrap_dim_index 处理,它有 #[track_caller],但 sum_dim 没有,位置停在 numeric.rs。负数维度是合法的(-1 是最后一维),越界时的 BoundsError 文本来自 AsIndex 的转换。

要找到自己代码里出错的那一行,用 RUST_BACKTRACE=1:


thread 'main' (3206527) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/numeric.rs:842:9:
=== Tensor Operation Error ===
  Operation: 'Matmul'
  Reason:
    1. The inner dimension of matmul should be the same, but got 3 and 2. Lhs shape Shape { dims: [2, 3] }, rhs shape Shape { dims: [2, 3] }. 

stack backtrace:
   0: __rustc::rust_begin_unwind
             at /rustc/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/std/src/panicking.rs:679:5
   1: core::panicking::panic_fmt
             at /rustc/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/core/src/panicking.rs:80:14
   2: core::panicking::panic_display::<alloc::string::String>
             at ~/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/panicking.rs:259:5
   3: <burn_tensor::tensor::api::base::Tensor<2>>::matmul
             at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/check.rs:1382:17
   4: tensor_checks::main
             at ./src/main.rs:13:17
   5: <fn() as core::ops::function::FnOnce<()>>::call_once
             at ~/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

第 4 帧才是用户代码。第 3 帧的位置显示成 check.rs:1382,那是 check! 宏里 core::panic! 所在的行,调试信息把展开后的代码记在宏定义处。

同样的程序用 --release 构建,报错一字不差:


thread 'main' (3206580) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/numeric.rs:842:9:
=== Tensor Operation Error ===
  Operation: 'Matmul'
  Reason:
    1. The inner dimension of matmul should be the same, but got 3 and 2. Lhs shape Shape { dims: [2, 3] }, rhs shape Shape { dims: [2, 3] }. 

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101
📝NOTE
冷知识:一维张量的 matmul 曾经在三个后端上各错各的

Tensor::matmul 对所有 D 都存在,Tensor<1> 也能调。#5580「feat(tensor): validate matmul rank in TensorCheck」之前,TensorCheck::matmul 在 D < 2 时直接返回,提交说明写的是当时的后果:「ndarray panics with "attempt to subtract with overflow" (ndims - 2 underflows), tch returns a 0-dim dot product, and cubecl has no valid kernel input」。修法是在检查里加一条「Matmul requires tensors with at least 2 dimensions」,参照的是已有的 tri() 检查。四天前的 #5555 出于同样的理由加了批维广播检查:「an incompatible batch dimension ... falls through to backend-specific panics with inconsistent messages」。两个 PR 都首次出现在 v0.22.0-pre.4。这正是检查放在 burn-tensor 而不是各个后端的原因:一处写,所有后端得到同一条报错。

设备:同一条检查,三种结局

二元运算和 matmul 都先调 binary_ops_device,它只是比较 lhs.device() != rhs.device()。Flex 和弃用的 ndarray、Flex 和 Metal:


thread 'main' (3206469) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/numeric.rs:40:9:
=== Tensor Operation Error ===
  Operation: 'Add'
  Reason:
    1. The provided tensors are not on the same device. Lhs tensor device Device<Flex(Cpu)>, Rhs tensor device Device<NdArray(Cpu)>. 

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

thread 'main' (3206471) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/numeric.rs:842:9:
=== Tensor Operation Error ===
  Operation: 'Matmul'
  Reason:
    1. The provided tensors are not on the same device. Lhs tensor device Device<Flex(Cpu)>, Rhs tensor device Device<Cube(Wgpu(WgpuDevice { kind: DefaultDevice, backend: Metal }))>. 

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

Device::default() 和 Device::metal(DeviceKind::DefaultDevice) 在这台 Mac 上用的是同一块 GPU,但 Device 的相等把图形 API 算在内(见 Device),检查同样拦下:


thread 'main' (3206479) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/numeric.rs:40:9:
=== Tensor Operation Error ===
  Operation: 'Add'
  Reason:
    1. The provided tensors are not on the same device. Lhs tensor device Device<Cube(Wgpu(WgpuDevice { kind: DefaultDevice, backend: Auto }))>, Rhs tensor device Device<Cube(Wgpu(WgpuDevice { kind: DefaultDevice, backend: Metal }))>. 

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

不是每个 op 都查设备。TensorCheck::cat 只查「列表非空」和「除拼接维以外形状相同」。把 Flex 和 Metal 上的两个张量拼起来,检查放行,panic 发生在 Dispatch 里:

let a = Tensor::<1>::ones([2], &flex);
let b = Tensor::<1>::ones([2], &metal);
let c = Tensor::cat(vec![a, b], 0);

thread 'main' (3206492) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-dispatch-0.22.1/src/ops/tensor.rs:11:1:
input tensor `__tensor` is on the wrong backend
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

位置 burn-dispatch/src/ops/tensor.rs:11:1 是 #[backend_dispatch] 属性所在的行,__tensor 是宏生成的变量名:分派宏按第一个张量选定后端,后面的张量不是这个后端就 panic(宏怎么生成这个 match 见 Dispatch)。报错里没有 op 名,也没有两个设备分别是什么。

两个张量都在 Cube 后端上、只是设备不同时,分派也拦不住:

let a = Tensor::<1>::ones([2], &Device::default());
let b = Tensor::<1>::zeros([2], &metal);
let c = Tensor::cat(vec![a, b], 0);
println!("cat: {:?} on {:?}", c.to_data().as_slice::<f32>().unwrap(), c.device());

thread 'DSU-100-0' (3206506) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-ir-0.22.1/src/handle.rs:333:32:
Should have handle for tensor TensorId { value: 1 }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

thread 'main' (3206499) panicked at src/main.rs:63:39:
Error while reading data: use `try_to_data` instead to catch the error at runtime: An error happened during execution
Caused by:
  Should have handle for tensor TensorId { value: 1 }
exit=101

cat 本身没有报错,op 被 fusion 收进了流。读数据时流开始执行,一个名为 DSU-100-0 的后台线程在 burn-ir 的 handle.rs 里 panic(fusion 用这个容器按 id 管理张量句柄):句柄表里找不到另一个设备上的那个张量。主线程拿到的是 ExecutionError,to_data 再把它 expect 成 panic。出错的 op 和报错的位置隔了一行代码、一个线程,fusion 为什么会这样见 Fusion 装饰器。

第三种结局是不报错。Device 的相等不看 autodiff 标记,所以一个 .autodiff() 设备上的张量和同一硬件上的普通张量可以直接运算:

let a = Tensor::<1>::from_floats([1.0, 2.0], &flex.clone().autodiff()).require_grad();
let b = Tensor::<1>::from_floats([3.0, 4.0], &flex);
let c = a.clone() * b;
let grads = c.sum().backward();
mixed: [3.0, 8.0] on Device<Autodiff { device: Flex(Cpu), checkpointing: Disabled }>
grad a: Some(TensorData { bytes: Bytes { data: [0, 0, 64, "..."], len: 8 }, shape: Shape { dims: [2] }, dtype: F32 })
case autodiff-mix: no panic
exit=0

结果在 autodiff 设备上,a 有梯度(字节 [0, 0, 64, 64] 是 f32 的 3.0,即 b 的第一个元素)。没有追踪的 b 被当作常量。

📝NOTE
没有蠢问题

问: cat 的两个输入形状都对,为什么还会 panic,而且报错和 add 的完全不是一个样子?

答: 因为设备检查是按 op 逐个写进 TensorCheck 的,TensorCheck::cat 没有写:它只查列表非空和形状。stack 先调 TensorCheck::stack 再调 cat,同样没有设备检查。没被 TensorCheck 拦下的跨设备调用要靠后面的层兜底:后端不同,#[backend_dispatch] 生成的 match 发现张量不在选定的后端上,panic 出一句不带 op 名的「wrong backend」;后端相同、设备不同,match 放行,就看后端自己怎么处理,fusion 下是在执行时出错。所以把来源不同的张量放进一个 Vec 交给 cat / stack 之前,先统一 to_device。

切片:越界不报错

TensorCheck::slice 里有一条「The provided slice has a range that exceeds the current tensor size」,但通过 tensor.slice(..) 永远走不到它。原因在调用顺序:Tensor::slice 先调 slices.into_slices(&shape),再做检查;而 burn-std 的 into_slices 已经把范围截断到形状以内:

impl<S: Into<Slice> + Clone> SliceArg for &[S] {
    fn into_slices(self, shape: &Shape) -> Vec<Slice> {
        assert!(
            self.len() <= shape.num_dims(),
            "Too many slices provided for shape, got {} but expected at most {}",
            self.len(),
            shape.num_dims()
        );

        shape
            .iter()
            .enumerate()
            .map(|(i, dim_size)| {
                let slice = if i >= self.len() {
                    Slice::full()
                } else {
                    self[i].clone().into()
                };
                // Apply shape clamping by converting to range and back
                let clamped_range = slice.to_range(*dim_size);
                Slice::new(
                    clamped_range.start as isize,
                    Some(clamped_range.end as isize),
                    slice.step(),
                )
            })
            .collect::<Vec<_>>()
    }
}

截断是有意的:Slice 的文档写着「Indices are clamped to valid ranges」,burn-backend-tests 里有一个测试就叫 clamp_when_slice_exceeds_dimension。TensorCheck::slice 里同一条消息在 slice_assign 的「Range Assign」检查里也有一份。

❗IMPORTANT
动脑筋:形状 [2, 3] 的张量,slice(s![0..2, 0..5]) 和 slice(s![0..9, 7..9]) 分别得到什么形状?会 panic 吗?平台和构建同上。答案在页末。

切片里真正会 panic 的是另外几种情况,报错都不是 TensorCheck 的格式:切片个数比秩多时是上面那条 assert!;步长为 0 时,s! 宏构造 Slice 的 from_range_stepped 就断言失败了。slice_assign 的值形状和切出来的形状对不上,才是 TensorCheck 的格式:


thread 'main' (3206519) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-std-0.22.1/src/tensor/slice.rs:23:9:
Too many slices provided for shape, got 2 but expected at most 1
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

thread 'main' (3206515) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-std-0.22.1/src/tensor/slice.rs:468:9:
Step cannot be zero
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

thread 'main' (3206517) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/base.rs:1444:9:
=== Tensor Operation Error ===
  Operation: 'Slice Assign'
  Reason:
    1. The value tensor must match the amount of elements selected with the slices array The slice with range (0..3) and step 1 selects 3 elements but the value tensor has 5 elements at dimension 1. Current tensor shape Shape { dims: [2, 3] }, value tensor shape Shape { dims: [2, 5] }. 

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

assert_shape!、s! 与 einsum!

TensorCheck 查的是一个 op 的输入是否自洽,查不了「这个张量是不是我以为的形状」。0.22 加了 assert_shape!(和只在 debug 下生效的 debug_assert_shape!)做这件事。它的秩在编译期检查(展开成对 Tensor::dims 的调用,返回 [usize; D]),各维大小在运行期检查,release 下也开着:

let x = Tensor::<2>::zeros([2, 3], &flex);
let [rows, _] = x.dims();
assert_shape!(x, [rows, 99]);

thread 'main' (3206523) panicked at src/main.rs:18:31:
assert_shape!(x, [rows, 99]): axis 1 expected 99, got 3 (dims [2, 3])
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

它是宏,panic 位置是用户代码的那一行。模式长度和秩不一致时编译不过(tensor-kind-err crate,cargo build --bin shapeerr):

error[E0308]: mismatched types
   --> src/bin/shapeerr.rs:5:3
    |
  5 |   assert_shape!(x, [2, 3, 1]);
    |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   |
    |   expected `3`, found `2`
    |   arguments to this function are incorrect
    |
    = note: expected reference `&Tensor<3, _>`
               found reference `&Tensor<2, burn::tensor::Float>`
note: method defined here
   --> ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/burn-tensor-0.22.1/src/tensor/api/base.rs:368:12
    |
368 |     pub fn dims(&self) -> [usize; D] {
    |            ^^^^
    = note: this error originates in the macro `$crate::__assert_shape` which comes from the expansion of the macro `assert_shape` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0308`.
error: could not compile `tensor-kind-err` (bin "shapeerr") due to 1 previous error

s! 来自 burn-std,把 0..2、..、-1、0..10;2 这些写法变成 Slice 数组;einsum! 是爱因斯坦求和的宏形式(#5583),展开在 burn-derive 里,本卷不展开讲。

Result 还是 panic

除了上面这些 panic,burn-tensor 里返回 Result 的地方集中在三类:读数据、设备同步、设备设置。

场景API失败时
op 的输入形状、秩、设备几乎所有张量方法TensorCheck panic
读回数据into_data / to_dataexpect 成 panic,带 #[track_caller]
读回数据try_into_data / try_to_data / into_data_asyncErr(ExecutionError)
读成标量、转成指定元素类型try_into_scalar、try_to_data_as、try_to_vec_asErr(TensorReadError):Execution、Data(DataError)、InvalidShape
同步、刷新、计时Device::sync / flush / profileErr(ExecutionError)
改默认 dtypeDevice::configureErr(DeviceError):UnsupportedDType、AlreadyInitialized

ExecutionError 有三个变体:WithContext、Generic(带回溯)和 DevicePoisoned。最后一个表示内核出了驱动无法恢复的故障,之后这个设备上的每个操作都会失败,只能重启进程。TensorReadError 在 ExecutionError 外面再加上数据层的错误。

执行期错误为什么只能在读数据时才拿到?因为 GPU 后端和 fusion 都是先提交、后执行的:a + b 返回时内核可能还没跑,错误只能挂在之后某次同步上。try_into_data 的文档说它返回「any error that might have occurred since the last time the device was synchronized」。上面 cat 的例子就是这样:错误发生在 cat,报告在 to_data。

带 try_ 的读取和不带的对照:

let a = Tensor::<1>::ones([3], &flex);
println!("try_into_scalar: {:?}", a.clone().try_into_scalar::<f32>());
println!("try_to_data: {:?}", a.try_to_data().map(|d| d.shape().clone()));
let i = Tensor::<1, Int>::ones([1], &flex);
println!("try_to_vec_as::<f32> on Int: {:?}", i.try_to_vec_as::<f32>());
let _ = Tensor::<1>::ones([3], &flex).into_scalar::<f32>();
try_into_scalar: Err(InvalidShape { expected: 1, actual: 3 })
try_to_data: Ok(Shape { dims: [3] })
try_to_vec_as::<f32> on Int: Ok([1.0])

thread 'main' (3206525) panicked at src/main.rs:111:45:
Error while reading data: use `try_into_scalar` instead to catch the error at runtime: InvalidShape { expected: 1, actual: 3 }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
exit=101

into_scalar 有 #[track_caller],panic 位置落在用户代码 src/main.rs,这一点和 TensorCheck 的 panic 不同。try_to_vec_as::<f32> 在 Int 张量上成功,说明它会做元素类型转换,不是只比较 dtype。

动脑筋答案

s![0..2, 0..5] on [2, 3] -> Shape { dims: [2, 3] }
s![0..9, 7..9] on [2, 3] -> Shape { dims: [2, 0] }
case slice: no panic
exit=0

两次都不 panic。s![0..2, 0..5] 的第二维被截成 0..3,结果就是原张量的形状 [2, 3];s![0..9, 7..9] 的第一维截成 0..2,第二维的起点和终点都截成 3,得到空范围 3..3,结果形状 [2, 0]。空切片是允许的(检查函数里的注释说这和 PyTorch 一致,也是 ONNX 动态切片需要的),Tensor::slice 看到输出有一维是 0,直接建一个空张量返回。所以写错的切片范围不会报错,只会悄悄得到一个更小甚至为空的张量。

小结

  • TensorCheck 在调用离开泛型之前检查形状、秩和设备,一个 op 的所有问题编号列出后一次 panic;check! 没有 debug_assertions 条件,release 下也开着。
  • panic 位置停在 burn-tensor 的源码里,用户代码的行号要看 RUST_BACKTRACE=1;into_data、into_scalar 这类带 #[track_caller] 的方法例外。
  • 设备检查只覆盖写了它的 op:cat 跨后端在分派宏里 panic,同后端跨设备在后台线程里出错、到读数据时才以 ExecutionError 出现;autodiff 和非 autodiff 的张量在同一硬件上可以直接运算。
  • 切片范围越界被截断,不报错;切片个数过多和步长为 0 在 burn-std 里断言失败。
  • assert_shape! 的秩在编译期查、尺寸在运行期查;Result 只出现在读数据、设备同步和设备设置三类 API 上。

💬 关联讨论

发起讨论
加载中...