如何确保我的功能将收到有效长度的Vec?

时间:2018-07-24 08:06:36

标签: rust

是否可以指定函数的Vec参数具有一定的长度?考虑骰子的可能值:

fn new(d_type: DiceType, face_vals: /*Vec<u32> with len() == 2/4/6/8/10/12/20*/) -> Dice {...}

我正在写一些东西,让您创建具有指定面值的多面骰子(通常RPG大小:2、4、6等)。我记得当您在不使用unsafe关键字的情况下调用Rust函数时,用户应该可以调用它,但是他们希望自己不用担心失败,因此只需检查函数的有效性并返回一些“,您就搞砸了“ 错误是严重的Rust。

我该如何实现?

这是我正在处理的代码的一部分:

pub enum DiceType {
    D2,
    D4,
    D6,
    D8,
    D10,
    D10P,
    D12,
    D20,
}

pub struct Dice {
    dice_type: DiceType,
    face_count: usize,
    face_values: Vec<u32>,
}

impl Dice {
    pub fn new(d_type: DiceType, face_vals: Vec<u32>) -> Dice {
        let mut retval;

        //Reject if not a valid dice type 2, 4, 6, 8, 10, 12, or 20
        //I really shouldn't be doing this should I?
        if Dice::valid_dice(d_type, face_vals) {
            retval = Dice {
                dice_type: d_type,
                face_count: face_vals.len(),
                face_values: face_vals,
            }
        } else {
            //User wont know they got an error
            //Really shouldn't need to go here. How do I avoid needing
            //error checking?
            retval = Dice {
                dice_type: None,
                face_count: 2,
                face_values: face_vals,
            };
        }

        retval
    }
}

答案

可接受的答案表明可以很好地利用结果来返回值,但是响应使我开始思考如何使代码更灵活,同时仍然具有可以保证单卷溢出安全性的硬上限,因此我切出了一堆代码,并提出了以下内容,可以让您生成每卷介于1-10,000之间的骰子骰,并使用乘数来获得额外骰子。

const MAX_FACE_VALUE: u32 = 100000;
const MAX_FACE_COUNT: u32 = 10000;
const MAX_ROLL_COUNT: u32 = 9999;

pub struct Dice {
    face_count: usize,
    face_values: Vec<u32>,
}

impl Dice {
    pub fn new(mut face_vals: Vec<u32>) -> Self {
        //User may not have values greater than 100,000
        //Index access is safe since we use the for _ in _
        for x in 0..face_vals.len() {
            if face_vals[x] > MAX_FACE_VALUE {
                //Enforce the limit
                face_vals[x] = MAX_FACE_VALUE;
            }
        }

        //User may not have more than 10,000 faces
        if face_vals.len() > MAX_FACE_COUNT as usize {
            let new_vals: Vec<u32> = face_vals.split_off(MAX_FACE_COUNT as usize);
            Dice {
                face_count: MAX_FACE_COUNT as usize,
                face_values: new_vals,
            }
        } else if face_vals.len() == 0 {
            //No 0 sided dice allowed
            Dice {
                face_count: 1,
                face_values: vec![1],
            }
        } else {
            //Normal range
            Dice {
                face_count: face_vals.len(),
                face_values: face_vals,
            }
        }
    }
}

2 个答案:

答案 0 :(得分:7)

您应该使用带有变体的枚举,该变体具有固定长度的相应数组:

#[derive(Clone, Copy)]
pub enum Dice {
    D2([u32; 2]),
    D4([u32; 4]),
    D6([u32; 6]),
    D8([u32; 8]),
    D10([u32; 10]),
    D10P([u32; 10]),
    D12([u32; 12]),
    D20([u32; 20]),
}

那么您就不能拥有无效的值:

fn take_a_dice(_dice: Dice) {
    //
}

fn main() {
    take_a_dice(Dice::D4([1, 2, 4, 8]));
}

答案 1 :(得分:3)

您应该使用Result来解释可能的错误输入:

use std::cmp::Ordering;

#[derive(Clone, Copy)]
pub enum DiceType {
    D2,
    D4,
    D6,
    D8,
    D10,
    D10P,
    D12,
    D20
}

pub struct Dice {
    dice_type: DiceType,
    // no need for face_count, it's a method of DiceType
    face_values: Vec<u32>
}

// an error for invalid face value inputs
enum DiceError {
    TooFewFaceValues,
    TooManyFaceValues
}

impl DiceType {
    fn face_count(&self) -> usize {
        match self {
            DiceType::D2 => 2,
            DiceType::D4 => 4,
            _            => unimplemented!() // TODO: account for all the other variants
        }
    }
}

impl Dice {
    fn new(dice_type: DiceType, face_values: &[u32]) -> Result<Self, DiceError> {
        match face_values.len().cmp(&dice_type.face_count()) {
            Ordering::Less    => Err(DiceError::TooFewFaceValues),
            Ordering::Greater => Err(DiceError::TooManyFaceValues),
            Ordering::Equal   => Ok(
                Dice {
                    dice_type,
                    face_values: Vec::from(face_values)
                }
            )
        }
    }
}