Skip to main content

regit_identifiers/
bic.rs

1// Copyright 2026 Regit.io — Nicolas Koenig
2// SPDX-License-Identifier: Apache-2.0
3
4//! BIC — Business Identifier Code (ISO 9362), the SWIFT address of a
5//! financial institution.
6//!
7//! A BIC identifies a bank or other institution on the SWIFT network. It is
8//! either **8** characters (an institution's primary office) or **11**
9//! characters (the same with an explicit branch suffix), in up to four
10//! segments:
11//!
12//! ```text
13//!   D E U T D E F F 5 0 0
14//!   └──┬──┘ └┬┘ └┬┘ └─┬─┘
15//!     │      │   │     └ branch       [8..11]  three [A-Z0-9], 11-char only
16//!     │      │   └─────── location    [6..8]   two [A-Z0-9]
17//!     │      └─────────── country     [4..6]   ISO 3166-1 alpha-2 letters
18//!     └────────────────── institution [0..4]   four letters
19//! ```
20//!
21//! - The **institution code** is four letters naming the institution.
22//! - The **country code** is an ISO 3166-1 alpha-2 code — see
23//!   [`crate::country`].
24//! - The **location code** is two `[A-Z0-9]` characters. Its second character
25//!   carries a convention: `0` marks a test/training BIC and `1` a passive
26//!   SWIFT participant.
27//! - The **branch code**, present only in an 11-character BIC, is three
28//!   `[A-Z0-9]` characters identifying a specific branch.
29//!
30//! A BIC has **no check digit**: [`Bic::parse`] enforces structure only —
31//! one of the two permitted lengths, the per-segment character set, and a
32//! country code that is a recognised ISO 3166-1 alpha-2 code.
33//!
34//! # References
35//!
36//! - ISO 9362, *Banking — Banking telecommunication messages — Business
37//!   identifier code (BIC)*.
38
39use crate::country;
40use crate::errors::ValidationError;
41
42/// A validated Business Identifier Code (ISO 9362).
43///
44/// A `Bic` can only be created by [`Bic::parse`] (or the explicitly unchecked
45/// [`Bic::from_bytes_unchecked`]), so a value of this type is a proof that 8
46/// or 11 characters form a structurally valid BIC. It stores the identifier
47/// inline as `[u8; 11]` with a `len` field; the unused tail bytes of an
48/// 8-character BIC are zeroed, so the derived `PartialEq`/`Eq`/`Hash` compare
49/// only the significant characters. It is `Copy` and allocates nothing.
50///
51/// # Examples
52///
53/// ```
54/// use regit_identifiers::Bic;
55///
56/// let bic = Bic::parse("DEUTDEFF500").unwrap();
57/// assert_eq!(bic.institution(), "DEUT");
58/// assert_eq!(bic.country_code(), "DE");
59/// assert_eq!(bic.location_code(), "FF");
60/// assert_eq!(bic.branch_code(), Some("500"));
61/// assert_eq!(bic.as_str(), "DEUTDEFF500");
62/// ```
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
64pub struct Bic {
65    /// The validated ASCII bytes, left-aligned; tail bytes past `len` zeroed.
66    bytes: [u8; Self::MAX_LENGTH],
67    /// The number of significant bytes — always either 8 or 11.
68    len: u8,
69}
70
71impl Bic {
72    /// The length of a BIC without a branch code.
73    pub const SHORT_LENGTH: usize = 8;
74
75    /// The length of a BIC with an explicit branch code.
76    pub const MAX_LENGTH: usize = 11;
77
78    /// Parses and validates a BIC.
79    ///
80    /// Validation is strict and structural — a BIC carries no check digit.
81    /// In order: the input must be exactly 8 or 11 characters; the
82    /// institution and country segments (characters 1–6) must each be an
83    /// upper-case ASCII letter; the location and branch segments must each be
84    /// an ASCII digit or upper-case letter; and the country segment must be a
85    /// recognised ISO 3166-1 alpha-2 code.
86    ///
87    /// # Errors
88    ///
89    /// - [`ValidationError::Structure`] with rule `"BIC length must be 8 or
90    ///   11"` if the input is neither 8 nor 11 characters.
91    /// - [`ValidationError::InvalidCharacter`] if a character falls outside
92    ///   the set its position allows (this also rejects lower-case input and
93    ///   any non-ASCII character).
94    /// - [`ValidationError::InvalidCountryCode`] if characters 5–6 are not a
95    ///   recognised ISO 3166-1 alpha-2 country code.
96    ///
97    /// # Examples
98    ///
99    /// ```
100    /// use regit_identifiers::Bic;
101    /// use regit_identifiers::errors::ValidationError;
102    ///
103    /// assert!(Bic::parse("DEUTDEFF").is_ok());
104    /// assert!(Bic::parse("DEUTDEFF500").is_ok());
105    ///
106    /// // A 9-character string is neither permitted length.
107    /// assert_eq!(
108    ///     Bic::parse("DEUTDEFF5"),
109    ///     Err(ValidationError::Structure { rule: "BIC length must be 8 or 11" }),
110    /// );
111    /// ```
112    pub fn parse(s: &str) -> Result<Self, ValidationError> {
113        // A BIC is exactly 8 or 11 characters; any other length is rejected.
114        let found = s.chars().count();
115        if found != Self::SHORT_LENGTH && found != Self::MAX_LENGTH {
116            return Err(ValidationError::Structure {
117                rule: "BIC length must be 8 or 11",
118            });
119        }
120        // Per-position character set: [0..6] are letters, [6..11] are
121        // [A-Z0-9]. A non-ASCII character fails both predicates and is
122        // rejected here.
123        for (i, ch) in s.chars().enumerate() {
124            let legal = if i < 6 {
125                ch.is_ascii_uppercase()
126            } else {
127                ch.is_ascii_uppercase() || ch.is_ascii_digit()
128            };
129            if !legal {
130                return Err(ValidationError::InvalidCharacter {
131                    position: i + 1,
132                    found: ch,
133                });
134            }
135        }
136        // Every character is ASCII, so the string is exactly `found` ASCII
137        // bytes; copy them into a zeroed buffer so the tail stays zero.
138        let mut bytes = [0u8; Self::MAX_LENGTH];
139        let src = s.as_bytes();
140        if let Some(slot) = bytes.get_mut(..found) {
141            slot.copy_from_slice(src);
142        }
143        // Characters 5–6 must be a recognised ISO 3166-1 alpha-2 code.
144        let country_code = core::str::from_utf8(&bytes[4..6]).unwrap_or("");
145        if !country::is_iso_country(country_code) {
146            return Err(ValidationError::InvalidCountryCode);
147        }
148        Ok(Self {
149            bytes,
150            len: u8::try_from(found).unwrap_or(0),
151        })
152    }
153
154    /// Validates a BIC without constructing one.
155    ///
156    /// Equivalent to `Bic::parse(s).map(|_| ())`; use it when only the
157    /// verdict is needed.
158    ///
159    /// # Errors
160    ///
161    /// Returns the same [`ValidationError`] variants as [`Bic::parse`].
162    ///
163    /// # Examples
164    ///
165    /// ```
166    /// use regit_identifiers::Bic;
167    ///
168    /// assert!(Bic::validate("CHASUS33").is_ok());
169    /// assert!(Bic::validate("CHASUS3").is_err());
170    /// ```
171    pub fn validate(s: &str) -> Result<(), ValidationError> {
172        Self::parse(s).map(|_| ())
173    }
174
175    /// Wraps raw bytes as a `Bic` without any validation.
176    ///
177    /// The caller asserts that the first `len` bytes hold the characters of a
178    /// valid BIC, that `len` is 8 or 11, and that every byte from `len`
179    /// onwards is zero. This exists for reconstructing a `Bic` from bytes
180    /// that were validated earlier; prefer [`Bic::parse`] for any untrusted
181    /// input.
182    ///
183    /// # Examples
184    ///
185    /// ```
186    /// use regit_identifiers::Bic;
187    ///
188    /// // The 3 trailing zero bytes are REQUIRED, not arbitrary padding —
189    /// // the derived `PartialEq`/`Eq`/`Hash` compare the full 11-byte buffer.
190    /// let bic = Bic::from_bytes_unchecked(*b"DEUTDEFF\0\0\0", 8);
191    /// assert_eq!(bic.as_str(), "DEUTDEFF");
192    /// ```
193    #[must_use]
194    pub const fn from_bytes_unchecked(bytes: [u8; Self::MAX_LENGTH], len: u8) -> Self {
195        Self { bytes, len }
196    }
197
198    /// Returns the number of characters in this BIC — 8 or 11.
199    ///
200    /// # Examples
201    ///
202    /// ```
203    /// use regit_identifiers::Bic;
204    ///
205    /// assert_eq!(Bic::parse("DEUTDEFF").unwrap().len(), 8);
206    /// assert_eq!(Bic::parse("DEUTDEFF500").unwrap().len(), 11);
207    /// ```
208    #[must_use]
209    #[inline]
210    pub fn len(&self) -> usize {
211        self.len as usize
212    }
213
214    /// Always `false` — a valid BIC is never empty (it is 8 or 11
215    /// characters). Provided for API consistency with [`Bic::len`].
216    ///
217    /// # Examples
218    ///
219    /// ```
220    /// use regit_identifiers::Bic;
221    ///
222    /// assert!(!Bic::parse("DEUTDEFF").unwrap().is_empty());
223    /// ```
224    #[must_use]
225    #[inline]
226    pub fn is_empty(&self) -> bool {
227        self.len == 0
228    }
229
230    /// Returns the BIC as a string slice.
231    ///
232    /// # Examples
233    ///
234    /// ```
235    /// use regit_identifiers::Bic;
236    ///
237    /// assert_eq!(Bic::parse("DEUTDEFF500").unwrap().as_str(), "DEUTDEFF500");
238    /// ```
239    #[must_use]
240    #[inline]
241    pub fn as_str(&self) -> &str {
242        core::str::from_utf8(self.as_bytes()).unwrap_or("")
243    }
244
245    /// Returns the BIC as its raw ASCII bytes — 8 or 11 bytes, with no
246    /// trailing zero padding.
247    ///
248    /// # Examples
249    ///
250    /// ```
251    /// use regit_identifiers::Bic;
252    ///
253    /// assert_eq!(Bic::parse("DEUTDEFF").unwrap().as_bytes(), b"DEUTDEFF");
254    /// ```
255    #[must_use]
256    #[inline]
257    pub fn as_bytes(&self) -> &[u8] {
258        self.bytes.get(..self.len()).unwrap_or(&[])
259    }
260
261    /// Returns the four-character institution code, characters 1–4.
262    ///
263    /// # Examples
264    ///
265    /// ```
266    /// use regit_identifiers::Bic;
267    ///
268    /// assert_eq!(Bic::parse("DEUTDEFF").unwrap().institution(), "DEUT");
269    /// ```
270    #[must_use]
271    #[inline]
272    pub fn institution(&self) -> &str {
273        core::str::from_utf8(&self.bytes[0..4]).unwrap_or("")
274    }
275
276    /// Returns the two-character ISO 3166-1 country code, characters 5–6.
277    ///
278    /// # Examples
279    ///
280    /// ```
281    /// use regit_identifiers::Bic;
282    ///
283    /// assert_eq!(Bic::parse("DEUTDEFF").unwrap().country_code(), "DE");
284    /// ```
285    #[must_use]
286    #[inline]
287    pub fn country_code(&self) -> &str {
288        core::str::from_utf8(&self.bytes[4..6]).unwrap_or("")
289    }
290
291    /// Returns the two-character location code, characters 7–8.
292    ///
293    /// # Examples
294    ///
295    /// ```
296    /// use regit_identifiers::Bic;
297    ///
298    /// assert_eq!(Bic::parse("DEUTDEFF").unwrap().location_code(), "FF");
299    /// ```
300    #[must_use]
301    #[inline]
302    pub fn location_code(&self) -> &str {
303        core::str::from_utf8(&self.bytes[6..8]).unwrap_or("")
304    }
305
306    /// Returns the three-character branch code, characters 9–11, or `None`
307    /// for an 8-character BIC with no branch suffix.
308    ///
309    /// # Examples
310    ///
311    /// ```
312    /// use regit_identifiers::Bic;
313    ///
314    /// assert_eq!(Bic::parse("DEUTDEFF500").unwrap().branch_code(), Some("500"));
315    /// assert_eq!(Bic::parse("DEUTDEFF").unwrap().branch_code(), None);
316    /// ```
317    #[must_use]
318    #[inline]
319    pub fn branch_code(&self) -> Option<&str> {
320        if self.has_branch() {
321            Some(core::str::from_utf8(&self.bytes[8..11]).unwrap_or(""))
322        } else {
323            None
324        }
325    }
326
327    /// Returns `true` if this BIC carries an explicit branch code, i.e. it is
328    /// 11 characters long.
329    ///
330    /// # Examples
331    ///
332    /// ```
333    /// use regit_identifiers::Bic;
334    ///
335    /// assert!(Bic::parse("DEUTDEFF500").unwrap().has_branch());
336    /// assert!(!Bic::parse("DEUTDEFF").unwrap().has_branch());
337    /// ```
338    #[must_use]
339    #[inline]
340    pub fn has_branch(&self) -> bool {
341        self.len() == Self::MAX_LENGTH
342    }
343
344    /// Returns `true` if this is a test/training BIC, i.e. the second
345    /// character of the location code (character 8) is `'0'`.
346    ///
347    /// # Examples
348    ///
349    /// ```
350    /// use regit_identifiers::Bic;
351    ///
352    /// assert!(Bic::parse("DEUTDEF0").unwrap().is_test_bic());
353    /// assert!(!Bic::parse("DEUTDEFF").unwrap().is_test_bic());
354    /// ```
355    #[must_use]
356    #[inline]
357    pub fn is_test_bic(&self) -> bool {
358        self.bytes[7] == b'0'
359    }
360
361    /// Returns `true` if this BIC belongs to a passive SWIFT participant,
362    /// i.e. the second character of the location code (character 8) is `'1'`.
363    ///
364    /// # Examples
365    ///
366    /// ```
367    /// use regit_identifiers::Bic;
368    ///
369    /// assert!(Bic::parse("DEUTDEF1").unwrap().is_passive());
370    /// assert!(!Bic::parse("DEUTDEFF").unwrap().is_passive());
371    /// ```
372    #[must_use]
373    #[inline]
374    pub fn is_passive(&self) -> bool {
375        self.bytes[7] == b'1'
376    }
377
378    /// Returns the second character of the location code (character 8 of
379    /// the BIC) — the "status character" by SWIFT convention.
380    ///
381    /// SWIFT attaches a convention to this character: `'0'` marks a test or
382    /// training BIC ([`Bic::is_test_bic`]) and `'1'` a passive participant
383    /// ([`Bic::is_passive`]). Any other value denotes a connected,
384    /// non-test, non-passive participant. Use this accessor when you want
385    /// the raw character; use the predicates when you only need a yes/no.
386    ///
387    /// # Examples
388    ///
389    /// ```
390    /// use regit_identifiers::Bic;
391    ///
392    /// assert_eq!(Bic::parse("DEUTDEFF").unwrap().location_status(), 'F');
393    /// assert_eq!(Bic::parse("DEUTDEF0").unwrap().location_status(), '0');
394    /// assert_eq!(Bic::parse("DEUTDEF1").unwrap().location_status(), '1');
395    /// ```
396    #[must_use]
397    #[inline]
398    pub fn location_status(&self) -> char {
399        char::from(self.bytes[7])
400    }
401}
402
403impl core::fmt::Display for Bic {
404    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
405        f.write_str(self.as_str())
406    }
407}
408
409impl core::str::FromStr for Bic {
410    type Err = ValidationError;
411
412    fn from_str(s: &str) -> Result<Self, Self::Err> {
413        Self::parse(s)
414    }
415}
416
417impl AsRef<str> for Bic {
418    fn as_ref(&self) -> &str {
419        self.as_str()
420    }
421}
422
423#[cfg(test)]
424mod tests {
425    use super::*;
426    use crate::test_support::display;
427    use core::str::FromStr;
428
429    /// Real, well-known BICs used as regression anchors.
430    const GOLDEN: &[&str] = &[
431        "DEUTDEFF",    // Deutsche Bank, Frankfurt (8 characters)
432        "DEUTDEFF500", // Deutsche Bank, Frankfurt, branch 500 (11 characters)
433        "CHASUS33",    // JPMorgan Chase Bank, New York
434        "BOFAUS3N",    // Bank of America, New York
435        "NDEAFIHH",    // Nordea Bank, Helsinki
436    ];
437
438    #[test]
439    fn parses_golden_bics() {
440        for &s in GOLDEN {
441            let bic = Bic::parse(s).unwrap_or_else(|e| panic!("{s} should parse: {e}"));
442            assert_eq!(bic.as_str(), s);
443        }
444    }
445
446    #[test]
447    fn segment_accessors_short() {
448        let bic = Bic::parse("DEUTDEFF").unwrap();
449        assert_eq!(bic.institution(), "DEUT");
450        assert_eq!(bic.country_code(), "DE");
451        assert_eq!(bic.location_code(), "FF");
452        assert_eq!(bic.branch_code(), None);
453        assert!(!bic.has_branch());
454        assert_eq!(bic.len(), 8);
455        assert_eq!(bic.as_bytes(), b"DEUTDEFF");
456    }
457
458    #[test]
459    fn segment_accessors_with_branch() {
460        let bic = Bic::parse("DEUTDEFF500").unwrap();
461        assert_eq!(bic.institution(), "DEUT");
462        assert_eq!(bic.country_code(), "DE");
463        assert_eq!(bic.location_code(), "FF");
464        assert_eq!(bic.branch_code(), Some("500"));
465        assert!(bic.has_branch());
466        assert_eq!(bic.len(), 11);
467        assert_eq!(bic.as_bytes(), b"DEUTDEFF500");
468    }
469
470    #[test]
471    fn length_constants() {
472        assert_eq!(Bic::SHORT_LENGTH, 8);
473        assert_eq!(Bic::MAX_LENGTH, 11);
474    }
475
476    #[test]
477    fn test_bic_flag() {
478        let bic = Bic::parse("DEUTDEF0").unwrap();
479        assert!(bic.is_test_bic());
480        assert!(!bic.is_passive());
481    }
482
483    #[test]
484    fn passive_participant_flag() {
485        let bic = Bic::parse("DEUTDEF1").unwrap();
486        assert!(bic.is_passive());
487        assert!(!bic.is_test_bic());
488    }
489
490    #[test]
491    fn live_bic_is_neither_test_nor_passive() {
492        let bic = Bic::parse("DEUTDEFF").unwrap();
493        assert!(!bic.is_test_bic());
494        assert!(!bic.is_passive());
495    }
496
497    #[test]
498    fn location_status_returns_eighth_character() {
499        assert_eq!(Bic::parse("DEUTDEFF").unwrap().location_status(), 'F');
500        assert_eq!(Bic::parse("DEUTDEF0").unwrap().location_status(), '0');
501        assert_eq!(Bic::parse("DEUTDEF1").unwrap().location_status(), '1');
502        // Same character in an 11-character BIC.
503        assert_eq!(Bic::parse("DEUTDEFF500").unwrap().location_status(), 'F');
504    }
505
506    #[test]
507    fn rejects_wrong_length() {
508        // Nine characters is neither 8 nor 11.
509        assert_eq!(
510            Bic::parse("DEUTDEFF5"),
511            Err(ValidationError::Structure {
512                rule: "BIC length must be 8 or 11",
513            })
514        );
515        // Ten characters likewise.
516        assert_eq!(
517            Bic::parse("DEUTDEFF50"),
518            Err(ValidationError::Structure {
519                rule: "BIC length must be 8 or 11",
520            })
521        );
522        // Empty input.
523        assert_eq!(
524            Bic::parse(""),
525            Err(ValidationError::Structure {
526                rule: "BIC length must be 8 or 11",
527            })
528        );
529        // Twelve characters.
530        assert_eq!(
531            Bic::parse("DEUTDEFF5000"),
532            Err(ValidationError::Structure {
533                rule: "BIC length must be 8 or 11",
534            })
535        );
536    }
537
538    #[test]
539    fn rejects_digit_in_institution() {
540        assert!(matches!(
541            Bic::parse("DEU1DEFF"),
542            Err(ValidationError::InvalidCharacter { position: 4, .. })
543        ));
544    }
545
546    #[test]
547    fn rejects_digit_in_country() {
548        // A digit anywhere in characters 5–6 must be rejected as a character
549        // error, before the country lookup.
550        assert!(matches!(
551            Bic::parse("DEUT1EFF"),
552            Err(ValidationError::InvalidCharacter { position: 5, .. })
553        ));
554    }
555
556    #[test]
557    fn rejects_lower_case() {
558        assert!(matches!(
559            Bic::parse("deutdeff"),
560            Err(ValidationError::InvalidCharacter { position: 1, .. })
561        ));
562    }
563
564    #[test]
565    fn rejects_unknown_country_code() {
566        // ZZ is two letters but not an assigned ISO 3166-1 code.
567        assert_eq!(
568            Bic::parse("DEUTZZFF"),
569            Err(ValidationError::InvalidCountryCode)
570        );
571    }
572
573    #[test]
574    fn rejects_substitute_prefix_as_country() {
575        // XS is an ISIN substitute prefix, not an ISO country, so it is not a
576        // valid BIC country code.
577        assert_eq!(
578            Bic::parse("DEUTXS33"),
579            Err(ValidationError::InvalidCountryCode)
580        );
581    }
582
583    #[test]
584    fn rejects_bad_character_in_branch() {
585        assert!(matches!(
586            Bic::parse("DEUTDEFF50/"),
587            Err(ValidationError::InvalidCharacter { position: 11, .. })
588        ));
589    }
590
591    #[test]
592    fn rejects_non_ascii_without_panic() {
593        // A multi-byte character must be rejected cleanly.
594        assert!(Bic::parse("DEUTDEFé").is_err());
595        assert!(Bic::parse("ÉEUTDEFF").is_err());
596        assert!(Bic::parse("DEUTDEFF50é").is_err());
597    }
598
599    #[test]
600    fn round_trips_through_str() {
601        for &s in GOLDEN {
602            assert_eq!(Bic::parse(s).unwrap().as_str(), s);
603        }
604    }
605
606    #[test]
607    fn from_str_matches_parse() {
608        assert_eq!(Bic::from_str("DEUTDEFF"), Bic::parse("DEUTDEFF"));
609        assert_eq!(Bic::from_str("DEUTDEFF500"), Bic::parse("DEUTDEFF500"));
610        assert!(Bic::from_str("nonsense!").is_err());
611    }
612
613    #[test]
614    fn display_renders_identifier() {
615        assert_eq!(
616            display(Bic::parse("DEUTDEFF").unwrap()).as_str(),
617            "DEUTDEFF"
618        );
619        assert_eq!(
620            display(Bic::parse("DEUTDEFF500").unwrap()).as_str(),
621            "DEUTDEFF500"
622        );
623    }
624
625    #[test]
626    fn as_ref_str() {
627        let bic = Bic::parse("CHASUS33").unwrap();
628        let s: &str = bic.as_ref();
629        assert_eq!(s, "CHASUS33");
630    }
631
632    #[test]
633    fn validate_agrees_with_parse() {
634        assert!(Bic::validate("BOFAUS3N").is_ok());
635        assert!(Bic::validate("BOFAUS3").is_err());
636    }
637
638    #[test]
639    fn from_bytes_unchecked_round_trip() {
640        let short = Bic::from_bytes_unchecked(*b"DEUTDEFF\0\0\0", 8);
641        assert_eq!(short, Bic::parse("DEUTDEFF").unwrap());
642        let long = Bic::from_bytes_unchecked(*b"DEUTDEFF500", 11);
643        assert_eq!(long, Bic::parse("DEUTDEFF500").unwrap());
644    }
645
646    #[test]
647    fn unused_tail_bytes_are_zeroed() {
648        // The tail of an 8-character BIC must be zero, so two equal 8-char
649        // BICs compare equal regardless of how they were built.
650        let parsed = Bic::parse("DEUTDEFF").unwrap();
651        let built = Bic::from_bytes_unchecked(*b"DEUTDEFF\0\0\0", 8);
652        assert_eq!(parsed, built);
653    }
654
655    #[test]
656    fn is_copy_and_eq_and_hashable() {
657        let a = Bic::parse("DEUTDEFF").unwrap();
658        let b = a; // Copy
659        assert_eq!(a, b);
660        assert_ne!(a, Bic::parse("CHASUS33").unwrap());
661        // An 8-char BIC and the 11-char BIC sharing its prefix differ.
662        assert_ne!(a, Bic::parse("DEUTDEFF500").unwrap());
663        // Usable as a map key (Eq + Hash) — checked by constructing a slice.
664        let keys = [a, b];
665        assert_eq!(keys[0], keys[1]);
666    }
667}