From 4053b21aa1958deefa50ff593203100f64f5ebed Mon Sep 17 00:00:00 2001 From: Michael Snoyman Date: Thu, 31 Dec 2020 11:36:04 +0200 Subject: [PATCH 1/8] Increase difficulty level of Xorcism #1040 --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 0f02de0c2..7e30d9cc2 100644 --- a/config.json +++ b/config.json @@ -814,7 +814,7 @@ "uuid": "0520ad82-75d9-450c-9267-d9758b3b0513", "core": false, "unlocked_by": "luhn", - "difficulty": 7, + "difficulty": 10, "topics": [ "bitwise", "generics", From 9490f5049fa58fd82d24f6f63422e977c649a95e Mon Sep 17 00:00:00 2001 From: Michael Snoyman Date: Thu, 31 Dec 2020 11:36:32 +0200 Subject: [PATCH 2/8] Add comment about lifetime of munge return #1040 --- exercises/xorcism/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/exercises/xorcism/README.md b/exercises/xorcism/README.md index e6185b19c..fbdf69930 100644 --- a/exercises/xorcism/README.md +++ b/exercises/xorcism/README.md @@ -55,6 +55,14 @@ These traits will be useful: - [`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) - [`Sized`](https://doc.rust-lang.org/std/marker/trait.Sized.html) +## Lifetime of `munge` return value + +Due to the usage of the `impl Trait` feature, lifetime management may be a bit +tricky when implementing the `munge` method. You may find it easier to write +your own `struct` with an `Iterator` implementation and return that concrete +type, at least to get started. Ultimately, it's a good idea to try and implement +the solution using `Iterator` combinators directly. + ## Bonus Tests This exercise contains bonus tests, behind the `io` feature flag. To enable From 682d9ad5cbc72b1eb921b2256598dff64a88a950 Mon Sep 17 00:00:00 2001 From: Michael Snoyman Date: Thu, 31 Dec 2020 11:36:52 +0200 Subject: [PATCH 3/8] Begin tests with sanity of munge_in_place #1040 --- exercises/xorcism/tests/xorcism.rs | 43 +++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/exercises/xorcism/tests/xorcism.rs b/exercises/xorcism/tests/xorcism.rs index 1ce62e95b..c85444164 100644 --- a/exercises/xorcism/tests/xorcism.rs +++ b/exercises/xorcism/tests/xorcism.rs @@ -6,7 +6,48 @@ use std::io::{Read, Write}; use xorcism::Xorcism; #[test] -fn identity() { +fn munge_in_place_identity() { + let mut xs = Xorcism::new(&[0]); + let input = "This is super-secret, cutting edge encryption, folks.".as_bytes(); + let mut output = input.to_owned(); + xs.munge_in_place(&mut output); + + assert_eq!(&input, &output); +} + +#[test] +#[ignore] +fn munge_in_place_roundtrip() { + let mut xs1 = Xorcism::new(&[1, 2, 3, 4, 5]); + let mut xs2 = Xorcism::new(&[1, 2, 3, 4, 5]); + let input = "This is super-secret, cutting edge encryption, folks.".as_bytes(); + let mut cipher = input.to_owned(); + xs1.munge_in_place(&mut cipher); + assert_ne!(&input, &cipher); + let mut output = cipher; + xs2.munge_in_place(&mut output); + assert_eq!(&input, &output); +} + +#[test] +#[ignore] +fn munge_in_place_stateful() { + let mut xs = Xorcism::new(&[1, 2, 3, 4, 5]); + let input = "This is super-secret, cutting edge encryption, folks.".as_bytes(); + + let mut cipher1 = input.to_owned(); + let mut cipher2 = input.to_owned(); + xs.munge_in_place(&mut cipher1); + xs.munge_in_place(&mut cipher2); + + assert_ne!(&input, &cipher1); + assert_ne!(&input, &cipher2); + assert_ne!(&cipher1, &cipher2); +} + +#[test] +#[ignore] +fn munge_identity() { let mut xs = Xorcism::new(&[0]); let data = "This is super-secret, cutting edge encryption, folks."; From fcf3415e6b757e582b019c57120a1acf620f4cf6 Mon Sep 17 00:00:00 2001 From: Michael Snoyman Date: Thu, 31 Dec 2020 11:51:14 +0200 Subject: [PATCH 4/8] Bypass need for ExactSizeIterator #1040 --- exercises/xorcism/README.md | 1 - exercises/xorcism/example.rs | 79 ++++++------------------------ exercises/xorcism/src/lib.rs | 10 +--- exercises/xorcism/tests/xorcism.rs | 8 --- 4 files changed, 17 insertions(+), 81 deletions(-) diff --git a/exercises/xorcism/README.md b/exercises/xorcism/README.md index fbdf69930..30b5b93c4 100644 --- a/exercises/xorcism/README.md +++ b/exercises/xorcism/README.md @@ -51,7 +51,6 @@ These traits will be useful: - [`AsRef`](https://doc.rust-lang.org/std/convert/trait.AsRef.html) - [`Borrow`](https://doc.rust-lang.org/std/borrow/trait.Borrow.html) -- [`ExactSizeIterator`](https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html) - [`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) - [`Sized`](https://doc.rust-lang.org/std/marker/trait.Sized.html) diff --git a/exercises/xorcism/example.rs b/exercises/xorcism/example.rs index 8e3644682..fc89222ff 100644 --- a/exercises/xorcism/example.rs +++ b/exercises/xorcism/example.rs @@ -17,48 +17,18 @@ pub struct Xorcism<'a> { pos: usize, } -/// For composability, it is important that `munge` returns an iterator compatible with its input. -/// -/// However, `impl Trait` syntax can specify only a single non-auto trait. -/// Therefore, we define this output trait with generic implementations on all compatible types, -/// and return that instead. -pub trait MungeOutput: Iterator + ExactSizeIterator {} -impl MungeOutput for T where T: Iterator + ExactSizeIterator {} - -/// WARNING: This could be construed as abusing the type system -/// -/// We need to be able to assert that a particular iterator has -/// an exact size. We need this because `iter::Zip` implements -/// `ExactSizeIterator` only if both its inputs implement `ExactSizeIterator`, evne though -/// it will always terminate on the shorter of them. -/// -/// We wouldn't need this type if negative trait bounds were possible, but it looks like -/// even in a post-chalk world, those are not likely to be added to the language, as they -/// could make new trait implementations into a breaking change. -/// -/// Essentially, this type is used to assert that an infinite iterator has an exact size, -/// of "the biggest number". -struct AssertExactSizeIterator(I); - -impl Iterator for AssertExactSizeIterator -where - I: Iterator, -{ - type Item = ::Item; - - fn next(&mut self) -> Option { - self.0.next() - } - - fn size_hint(&self) -> (usize, Option) { - // oops, all 1s - const SIZE: usize = !0; - (SIZE, Some(SIZE)) +/// Ideally this would be a method, but we run into lifetime +/// issues with that. For more information, see: +/// https://github.com/rust-lang/rust/issues/80518 +fn next_key_byte(key: &[u8], pos: &mut usize) -> u8 { + let b = key[*pos]; + *pos += 1; + if *pos >= key.len() { + *pos = 0; } + b } -impl ExactSizeIterator for AssertExactSizeIterator where I: Iterator {} - impl<'a> Xorcism<'a> { /// Create a new Xorcism munger from a key pub fn new(key: &'a Key) -> Xorcism<'a> @@ -69,29 +39,13 @@ impl<'a> Xorcism<'a> { Xorcism { key, pos: 0 } } - /// Increase the stored pos by the specified amount, returning the old value. - fn incr_pos(&mut self, by: usize) -> usize { - let old_pos = self.pos; - self.pos += by; - old_pos - } - - /// Produce the key iterator, offset by `pos`. - fn key<'b>(&mut self, pos: usize) -> impl 'b + MungeOutput - where - 'a: 'b, - { - AssertExactSizeIterator(self.key.iter().copied().cycle().skip(pos)) - } - /// XOR each byte of the input buffer with a byte from the key. /// /// Note that this is stateful: repeated calls are likely to produce different results, /// even with identical inputs. pub fn munge_in_place(&mut self, data: &mut [u8]) { - let pos = self.incr_pos(data.len()); - for (d, k) in data.iter_mut().zip(self.key(pos)) { - *d ^= k; + for d in data.iter_mut() { + *d ^= next_key_byte(self.key, &mut self.pos); } } @@ -99,16 +53,15 @@ impl<'a> Xorcism<'a> { /// /// Note that this is stateful: repeated calls are likely to produce different results, /// even with identical inputs. - pub fn munge<'b, Data, B>(&mut self, data: Data) -> impl 'b + MungeOutput + pub fn munge<'b, Data, B>(&'b mut self, data: Data) -> impl 'b + Iterator where - 'a: 'b, Data: IntoIterator, - ::IntoIter: 'b + ExactSizeIterator, + ::IntoIter: 'b, B: Borrow, { - let data = data.into_iter(); - let pos = self.incr_pos(data.len()); - data.zip(self.key(pos)).map(|(d, k)| d.borrow() ^ k) + let key = self.key; + let pos = &mut self.pos; + data.into_iter().map(move |d| d.borrow() ^ next_key_byte(key, pos)) } /// Convert this into a [`Writer`] diff --git a/exercises/xorcism/src/lib.rs b/exercises/xorcism/src/lib.rs index c91f4c19a..59adc045a 100644 --- a/exercises/xorcism/src/lib.rs +++ b/exercises/xorcism/src/lib.rs @@ -6,14 +6,6 @@ pub struct Xorcism<'a> { _phantom: std::marker::PhantomData<&'a u8>, } -/// For composability, it is important that `munge` returns an iterator compatible with its input. -/// -/// However, `impl Trait` syntax can specify only a single non-auto trait. -/// Therefore, we define this output trait with generic implementations on all compatible types, -/// and return that instead. -pub trait MungeOutput: Iterator + ExactSizeIterator {} -impl MungeOutput for T where T: Iterator + ExactSizeIterator {} - impl<'a> Xorcism<'a> { /// Create a new Xorcism munger from a key /// @@ -37,7 +29,7 @@ impl<'a> Xorcism<'a> { /// /// Should accept anything which has a cheap conversion to a byte iterator. /// Shouldn't matter whether the byte iterator's values are owned or borrowed. - pub fn munge(&mut self, data: Data) -> impl MungeOutput { + pub fn munge(&mut self, data: Data) -> impl Iterator { unimplemented!(); // this empty iterator silences a compiler complaint that // () doesn't implement ExactSizeIterator diff --git a/exercises/xorcism/tests/xorcism.rs b/exercises/xorcism/tests/xorcism.rs index c85444164..45c48e73b 100644 --- a/exercises/xorcism/tests/xorcism.rs +++ b/exercises/xorcism/tests/xorcism.rs @@ -57,14 +57,6 @@ fn munge_identity() { ); } -#[test] -#[ignore] -fn munge_output_has_len() { - let mut xs = Xorcism::new(&[0]); - let data = "The output must have a definite length"; - assert_eq!(xs.munge(data.as_bytes()).len(), 38); -} - #[test] #[ignore] fn statefulness() { From 1333cee4058ef75b38185aca1ab8cfde559640a7 Mon Sep 17 00:00:00 2001 From: Michael Snoyman Date: Thu, 31 Dec 2020 11:59:37 +0200 Subject: [PATCH 5/8] Fix rustfmt issue --- exercises/xorcism/example.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/xorcism/example.rs b/exercises/xorcism/example.rs index fc89222ff..ee5d6e0a0 100644 --- a/exercises/xorcism/example.rs +++ b/exercises/xorcism/example.rs @@ -61,7 +61,8 @@ impl<'a> Xorcism<'a> { { let key = self.key; let pos = &mut self.pos; - data.into_iter().map(move |d| d.borrow() ^ next_key_byte(key, pos)) + data.into_iter() + .map(move |d| d.borrow() ^ next_key_byte(key, pos)) } /// Convert this into a [`Writer`] From 166c18d5b531f1d3993da4e6cb9f53365cdbcdf8 Mon Sep 17 00:00:00 2001 From: Michael Snoyman Date: Fri, 1 Jan 2021 08:08:08 +0200 Subject: [PATCH 6/8] Update description.md --- exercises/xorcism/.meta/description.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/exercises/xorcism/.meta/description.md b/exercises/xorcism/.meta/description.md index 2e9436059..256e139b7 100644 --- a/exercises/xorcism/.meta/description.md +++ b/exercises/xorcism/.meta/description.md @@ -49,10 +49,17 @@ These traits will be useful: - [`AsRef`](https://doc.rust-lang.org/std/convert/trait.AsRef.html) - [`Borrow`](https://doc.rust-lang.org/std/borrow/trait.Borrow.html) -- [`ExactSizeIterator`](https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html) - [`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) - [`Sized`](https://doc.rust-lang.org/std/marker/trait.Sized.html) +## Lifetime of `munge` return value + +Due to the usage of the `impl Trait` feature, lifetime management may be a bit +tricky when implementing the `munge` method. You may find it easier to write +your own `struct` with an `Iterator` implementation and return that concrete +type, at least to get started. Ultimately, it's a good idea to try and implement +the solution using `Iterator` combinators directly. + ## Bonus Tests This exercise contains bonus tests, behind the `io` feature flag. To enable From aca3217a6ca7424270a6f557a1b453838c2934ce Mon Sep 17 00:00:00 2001 From: Michael Snoyman Date: Mon, 4 Jan 2021 08:29:18 +0200 Subject: [PATCH 7/8] Move to hints.md --- exercises/xorcism/.meta/description.md | 8 -------- exercises/xorcism/.meta/hints.md | 7 +++++++ exercises/xorcism/README.md | 16 ++++++++-------- 3 files changed, 15 insertions(+), 16 deletions(-) create mode 100644 exercises/xorcism/.meta/hints.md diff --git a/exercises/xorcism/.meta/description.md b/exercises/xorcism/.meta/description.md index 256e139b7..591325194 100644 --- a/exercises/xorcism/.meta/description.md +++ b/exercises/xorcism/.meta/description.md @@ -52,14 +52,6 @@ These traits will be useful: - [`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) - [`Sized`](https://doc.rust-lang.org/std/marker/trait.Sized.html) -## Lifetime of `munge` return value - -Due to the usage of the `impl Trait` feature, lifetime management may be a bit -tricky when implementing the `munge` method. You may find it easier to write -your own `struct` with an `Iterator` implementation and return that concrete -type, at least to get started. Ultimately, it's a good idea to try and implement -the solution using `Iterator` combinators directly. - ## Bonus Tests This exercise contains bonus tests, behind the `io` feature flag. To enable diff --git a/exercises/xorcism/.meta/hints.md b/exercises/xorcism/.meta/hints.md new file mode 100644 index 000000000..93c471c36 --- /dev/null +++ b/exercises/xorcism/.meta/hints.md @@ -0,0 +1,7 @@ +## Lifetime of `munge` return value + +Due to the usage of the `impl Trait` feature, lifetime management may be a bit +tricky when implementing the `munge` method. You may find it easier to write +your own `struct` with an `Iterator` implementation and return that concrete +type, at least to get started. Ultimately, it's a good idea to try and implement +the solution using `Iterator` combinators directly. diff --git a/exercises/xorcism/README.md b/exercises/xorcism/README.md index 30b5b93c4..3d1432d81 100644 --- a/exercises/xorcism/README.md +++ b/exercises/xorcism/README.md @@ -54,14 +54,6 @@ These traits will be useful: - [`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) - [`Sized`](https://doc.rust-lang.org/std/marker/trait.Sized.html) -## Lifetime of `munge` return value - -Due to the usage of the `impl Trait` feature, lifetime management may be a bit -tricky when implementing the `munge` method. You may find it easier to write -your own `struct` with an `Iterator` implementation and return that concrete -type, at least to get started. Ultimately, it's a good idea to try and implement -the solution using `Iterator` combinators directly. - ## Bonus Tests This exercise contains bonus tests, behind the `io` feature flag. To enable @@ -89,6 +81,14 @@ appropriate direction. They use these traits: - [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) - [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html) +## Lifetime of `munge` return value + +Due to the usage of the `impl Trait` feature, lifetime management may be a bit +tricky when implementing the `munge` method. You may find it easier to write +your own `struct` with an `Iterator` implementation and return that concrete +type, at least to get started. Ultimately, it's a good idea to try and implement +the solution using `Iterator` combinators directly. + ## Rust Installation Refer to the [exercism help page][help-page] for Rust installation and learning From cd93956c12ccf7295b8e6b88a46e7ad9e768865a Mon Sep 17 00:00:00 2001 From: Michael Snoyman Date: Mon, 4 Jan 2021 08:32:53 +0200 Subject: [PATCH 8/8] Add missing newline --- exercises/xorcism/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/exercises/xorcism/README.md b/exercises/xorcism/README.md index 3d1432d81..5a661915e 100644 --- a/exercises/xorcism/README.md +++ b/exercises/xorcism/README.md @@ -89,6 +89,7 @@ your own `struct` with an `Iterator` implementation and return that concrete type, at least to get started. Ultimately, it's a good idea to try and implement the solution using `Iterator` combinators directly. + ## Rust Installation Refer to the [exercism help page][help-page] for Rust installation and learning