في شهر ديسمبر ، صادفت مقالة رائعة للغاية باللغة الإنجليزية ، مكرسة لاستخدام نظام نوع اللغة لفئة أوسع من المهام ، لزيادة موثوقية التطبيقات وسهولة إعادة الهيكلة. لسوء الحظ ، في تلك اللحظة كنت مشغولًا جدًا في كتابة مقالات حول 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>
, head
— None
, , 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
, , , ! :
-, . , , ?
-, . , , , , , .
, , . , 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>);
, NonEmptyVec
— T
(, ) 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> { ... }
, !
:
, . , . , , .
, . , . , , , .
, . - , .
, , , , . , , - . , - !
, :
, . bool
, ( , , . .). , — , , ,
Result<(), Error>
. , - , , .
. , , , , , , . — -.
, , : .
, " " . , , , , , . (newtype)
, - .
, . , error "impossible"
- — , , . , , .
, -. , , — ! , .
. , — " " — . , , , . , — ! — . , — .
, , : Type Safety Back and Forth. , . Ghosts of Departed Proofs, , , .
, , , . , — , . , TypeDD, , , , . , , . , — .
, — , .