تحليل ، عدم التحقق من الصحة

في شهر ديسمبر ، صادفت مقالة رائعة للغاية باللغة الإنجليزية ، مكرسة لاستخدام نظام نوع اللغة لفئة أوسع من المهام ، لزيادة موثوقية التطبيقات وسهولة إعادة الهيكلة. لسوء الحظ ، في تلك اللحظة كنت مشغولًا جدًا في كتابة مقالات حول AF ، والتي كان من المهم للغاية كتابتها بينما الذكريات لا تزال حية. ولكن الآن ، عندما تعاملت مع هذه المهمة ، جئت أخيرًا لترجمة هذه المذكرة الرائعة. اللغة الأصلية للأمثلة هي هاسكل ، لكنني قررت نسخها إلى نقي ، من أجل وصول أوسع للجمهور. ومع ذلك ، فإن اللغة هنا غير مهمة تمامًا ، ونصائح هذه المقالة التي أستخدمها في التطوير اليومي على C # و TypeScript "الدنيوي" تمامًا ، لذلك إذا حاولت فقط كتابة رمز موثوق به ومدعوم ، فبغض النظر عن اللغة ، ستكون المقالة في الموضوع.


شكرا لك على التدقيق اللغوي والمساعدة في الترجمة. هيرولوت، funkill و andreevlex



خلال فترة طويلة إلى حد ما ، واجهت صعوبة في العثور على طريقة دقيقة وبسيطة لشرح التصميم المدفوع بالنوع (من الآن فصاعدًا - TypeDD ، تقريبًا Trans. ). غالبًا ما كانوا يسألونني "كيف توصلت إلى مثل هذا القرار؟" ، لكن لم يكن لدي إجابة مرضية. أعلم على وجه اليقين أنها لم تأت لي كرؤية: كان لدي عملية تصميم تكراري لم تجبرني على إخراج العمارة "الصحيحة" من لا شيء في الوقت الحالي ، ولكن حتى الآن لم أتمكن من تفسير هذه العملية للآخرين.


: JSON , , . , , , TypeDD, , , :


, .


TypeDD


, : TypeDD, , , . , . , , , .



, , , " ?". :


enum Void {}

fn foo(x: i32) -> Void

? , — , .. Void — , , Void. , , :


fn head<T>(xs: Vec<T>) -> T

. ? , - , , :


fn head<T>(xs: Vec<T>) -> T {
    match xs.as_slice() {
        [x, ..] => *x
    }
}

error[E0004]: non-exhaustive patterns: `&[]` not covered
 --> src/lib.rs:2:11
  |
2 |     match xs.as_slice() {
  |           ^^^^^^^^^^^^^ pattern `&[]` not covered
  |

, , , . , , [], . , .. , , , ! , , , ( , . .).



- . , . , " " - , . head, , .



, head , .. : , . , : . , , , , : , , . Rust Option:


fn head<T>(xs: Vec<T>) -> Option<T>

, headNone, , T:


fn head<T>(xs: Vec<T>) -> Option<T> {
    match xs.as_slice() {
        [x, ..] => Some(*x),
        [] => None,
    }
}

, ? , — … .


Option, , , head. . head None, , . , :


fn get_configuration_directories() -> Result<Vec<String>, &'static str> {
    let config_dirs_string = std::env::var("CONFIG_DIRS").map_err(|_| "cannot read env")?;
    let list: Vec<_> = config_dirs_string.split(',').map(|x| x.to_string()).collect();
    if list.is_empty() {
        return Err("CONFIG_DIRS cannot be empty");
    }
    Ok(list)
}

fn main() -> Result<(), &'static str> {
    let config_dirs = get_configuration_directories()?;
    match head(config_dirs) {
        Some(cacheDir) => initialize_cache(cacheDir),
        None => panic!("should never happen; already checked config_dirs is non-empty")
    }
    Ok(())
}

get_configuration_directories , . head main, , Option<&str> None, , , ! :


  1. -, . , , ?


  2. -, . , , , , , .


  3. , , . , get_configuration_directories , , , ? , main, "" , .



. None , get_configuration_directories .
, , .



, head , . , - : , , head , , , . ?


() head .


fn head<T>(xs: Vec<T>) -> T

, . , , : ( — Vec<T>). , head .


, , .
, NonEmptyVec . :


struct NonEmptyVec<T>(T, Vec<T>);

, NonEmptyVecT (, ) Vec<T>. , Vec<T> [], . , head :


fn head<T>(xs: NonEmptyVec<T>) -> T {
    xs.0
}

, , , , . :


fn get_configuration_directories() -> Result<NonEmptyVec<String>, &'static str> {
    let config_dirs_string = std::env::var("CONFIG_DIRS").map_err(|_| "cannot read env")?;
    let list: Vec<_> = config_dirs_string.split(',').map(|x| x.to_string()).collect();
    match non_empty(list) {
        Some(x) => Ok(x),
        None => Err("CONFIG_DIRS cannot be empty")
    }
}

fn main() -> Result<(), &'static str> {
    let config_dirs = get_configuration_directories()?;
    initialize_cache(head(config_dirs));
    Ok(())
}

, main ! , get_configuration_directories. NonEmptyVec Vec non_empty, :


fn non_empty<T>(list: Vec<T>) -> Option<NonEmptyVec<T>>

, Option , None : . , NonEmptyVec, ( !) , . , NonEmptyVec Vec<T> , .


head , :


  • , .


  • , get_configuration_directories , . , main , !



, head , non_empty:


fn old_head<T>(xs: Vec<T>) -> Option<T> {
    non_empty(xs).map(head)
}

, : head . , .



? , — , , . , : , , . :


fn validate_non_empty<T>(xs: Vec<T>) -> Result<(), UserError> {
    if !xs.is_empty() {
        Ok(())
    } else {
        Err(UserError::new("list cannot be empty"))
    }
}

fn parse_non_empty<T>(mut xs: Vec<T>) -> Result<NonEmptyVec<T>, UserError> {
    if !xs.is_empty() {
        let head = xs.remove(0);
        Ok(NonEmptyVec(head, xs))
    } else {
        Err(UserError::new("list cannot be empty"))
    }
}

: , , . : validate_non_empty (), , , parse_non_empty NonEmptyVec<T>, , . , parse_non_empty , validate_non_empty .


: validate_non_empty , parse_non_empty , . , parse_non_empty , , ", ". parse_non_empty. - , ? , , , , parse_non_empty , .


: ? , , , . , — — , - . , , , parse_non_empty : , .


: , , , ! Rust , :



: . - -, , . , , , , , .


, , : , . : - , . , NonEmpty : , .



, , , . , ? , , , ?


, . Ad-hoc , - . 2016 The Seven Turrets of Babel: A Taxonomy of LangSec Errors and How to Expunge Them :


— , , , ; , ( - ), "" .

, :


, . , - , .

, , , , , , , . — , , , — .


, — , , . , , , , "" . , , .


, : , , , . , .


, ,


, . ", , !", , , . "" "", "".


: .


, , , , , - . — , , :


fn check_no_duplicate_keys<K: Eq, V>(xs: &[(K,V)]) { ... }

"" — : . - , , , . , , , HashMap. , HashMap , , .


, , , , , . - , HashMap . , , , . check_no_duplicate_keys:


fn check_no_duplicate_keys<K: Eq, V>(xs: &[(K,V)]) -> HashMap<K,V> { ... }

, !


:


  1. , . , . , , .


  2. , . , . , , , .


    , . - , .



, , , , . , , - . , - !


, :


  • , . bool , ( , , . .). , — , , ,


  • Result<(), Error> . , - , , .


  • . , , , , , , . — -.


  • , , : .


    • . , , , .

  • , " " . , , , , , . (newtype) , - .



, . , error "impossible" - — , , . , , .



, -. , , — ! , .


. , — " " — . , , , . , — ! — . , — .


, , : Type Safety Back and Forth. , . Ghosts of Departed Proofs, , , .


, , , . , — , . , TypeDD, , , , . , , . , — .





, — , .


All Articles