2016-12-23 14 views
8

Tôi đang viết một phương pháp chu trình cho danh sách di chuyển một chỉ mục về phía trước hoặc ngược. Các mã sau đây được sử dụng để chu kỳ ngược:Panicked tại 'cố gắng trừ với tràn' khi đạp ngược trở lại mặc dù một danh sách

(i-1)%list_length 

Trong trường hợp này, i là loại usize, có nghĩa là nó là unsigned. Nếu i bằng 0, điều này dẫn đến lỗi 'cố gắng trừ với tràn'. Tôi đã cố gắng sử dụng các phương pháp truyền chính xác để giải quyết vấn đề này:

((i as isize)-1)%(list_length as isize)) as usize 

Điều này dẫn đến tràn số nguyên.

Tôi hiểu tại sao lỗi xảy ra và tại thời điểm này tôi đã giải quyết được vấn đề bằng cách kiểm tra xem chỉ mục có bằng 0 hay không, nhưng tôi đã tự hỏi có cách nào giải quyết nó hay không bằng cách truyền các biến đến đúng loại.

+5

Là một phần: Tôi không nghĩ bạn muốn làm điều đó *. -1% 10) 'là' -1', không phải '9'. '-1isize as usize' là' 18446744073709551615' (trên kiến ​​trúc 64 bit). –

+0

Ok, tôi không biết. Tôi nghĩ nó hoạt động như được mô tả trong [bài đăng này] (http://math.stackexchange.com/questions/519845/modulo-of-a-negative-number), nhưng bây giờ tôi thấy rằng nó được thực hiện như được mô tả trong [điều này post] (http://stackoverflow.com/questions/31210357/is-there-a-modulus-not-remainder-function-operation). Điều đó xóa nó lên! – blackplant

Trả lời

4

Như DK. points out, bạn không muốn gói ngữ nghĩa ở cấp số nguyên:

fn main() { 
    let idx: usize = 0; 
    let len = 10; 

    let next_idx = idx.wrapping_sub(1) % len; 
    println!("{}", next_idx) // Prints 5!!! 
} 

Thay vào đó, bạn muốn để sử dụng logic modulo để quấn xung quanh:

let next_idx = (idx + len - 1) % len; 

chỉ này hoạt động nếu len + idx là ít hơn so với tối đa của các loại - đây là nhiều dễ thấy hơn với một số u8 thay vì usize; chỉ cần đặt idx thành 200 và len thành 250.

Nếu bạn không thể đảm bảo rằng tổng của hai giá trị sẽ luôn nhỏ hơn giá trị tối đa, có thể tôi sẽ sử dụng họ hoạt động "đã chọn". Điều này thực hiện cùng một mức kiểm tra có điều kiện mà bạn đã đề cập mà bạn đã có, nhưng được gắn gọn gàng vào một dòng:

let next_idx = idx.checked_sub(1).unwrap_or(len - 1); 
3

Nếu mã của bạn có thể có hoạt động bị tràn, tôi khuyên bạn nên sử dụng Wrapping. Bạn không cần phải lo lắng về đúc hoặc tràn hoảng loạn khi bạn cho phép nó:

use std::num::Wrapping; 

let zero = Wrapping(0u32); 
let one = Wrapping(1u32); 

assert_eq!(std::u32::MAX, (zero - one).0); 
+2

Ngoài ra còn có [phương pháp vốn có trên mỗi loại] (https://doc.rust-lang.org/std/primitive.usize.html#method.wrapping_sub) cho gói số học. – Shepmaster

Các vấn đề liên quan