Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue287: create random password from a set of allowed characters #372

Merged
merged 1 commit into from Dec 12, 2017

Conversation

Projects
None yet
3 participants
@anna-liao
Copy link
Contributor

anna-liao commented Nov 27, 2017

fixes #287 r? @budziq

I categorized this example under text processing. Do you think that is the appropriate category?


[![rand-badge]][rand] [![cat-text-processing-badge]][cat-text-processing]

Randomly generates a string of given length ASCII characters in the range `A-Z, a-z, 0-9`.

This comment has been minimized.

@ludwigpacifici

ludwigpacifici Nov 27, 2017

Contributor

Can you give more details, such as gen_ascii_chars is the method which a hardcoded range of character.

Plus, I would show the choose method which is a user defined set of allowed characters. Maybe wait for the green flag of @budziq for this one.

This comment has been minimized.

@budziq

budziq Nov 28, 2017

Collaborator

Agreed on both arguments:

  • lets add links to key identifiers in the description.
  • I'd suggest to add two snippets here the second one being an iterator based in choose like the one used to implement gen_ascii_chars but taking arbitrary bytestring in the constructor, then used in identical fashion as the first trivial snippet. That would be very cool!
@budziq
Copy link
Collaborator

budziq left a comment

few suggestions

@@ -8,6 +8,7 @@
| [Generate random numbers within a range][ex-rand-range] | [![rand-badge]][rand] | [![cat-science-badge]][cat-science] |
| [Generate random numbers with given distribution][ex-rand-dist] | [![rand-badge]][rand] | [![cat-science-badge]][cat-science] |
| [Generate random values of a custom type][ex-rand-custom] | [![rand-badge]][rand] | [![cat-science-badge]][cat-science] |
| [Create random passwords from a set of allowed characters][ex-rand-passwd] | [![rand-badge]][rand] | [![cat-text-processing-badge]][cat-text-processing] |

This comment has been minimized.

@budziq

budziq Nov 28, 2017

Collaborator

I'd remove the "text-processing" category. We are not really doing any processing of input text.

use rand::{thread_rng, Rng};
fn main() {
let s: String =

This comment has been minimized.

@budziq

budziq Nov 28, 2017

Collaborator

lets run rustfmt on the snippet


[![rand-badge]][rand] [![cat-text-processing-badge]][cat-text-processing]

Randomly generates a string of given length ASCII characters in the range `A-Z, a-z, 0-9`.

This comment has been minimized.

@budziq

budziq Nov 28, 2017

Collaborator

Agreed on both arguments:

  • lets add links to key identifiers in the description.
  • I'd suggest to add two snippets here the second one being an iterator based in choose like the one used to implement gen_ascii_chars but taking arbitrary bytestring in the constructor, then used in identical fashion as the first trivial snippet. That would be very cool!

@anna-liao anna-liao force-pushed the anna-liao:issue287 branch from c99fdf8 to 69e730e Dec 3, 2017

@anna-liao

This comment has been minimized.

Copy link
Contributor Author

anna-liao commented Dec 3, 2017

r? @budziq @ludwigpacifici I added an example with choose. Was hoping to add punctuation into the bytestring, but the function call (commented out with //) doesn't work.

@budziq
Copy link
Collaborator

budziq left a comment

Thanks @anna-liao ! few suggestions below:

  • I would split this example into two separate code snippets.
@@ -1615,6 +1664,7 @@ fn main() {
[`BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html
[`BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
[`chain_err`]: https://docs.rs/error-chain/*/error_chain/index.html#chaining-errors
[`choose`]: https://docs.rs/rand/0.3.18/rand/trait.Rng.html#method.choose

This comment has been minimized.

@budziq

budziq Dec 6, 2017

Collaborator

lets use wildcard version numbers

@@ -1639,6 +1689,7 @@ fn main() {
[`File::try_clone`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.try_clone
[`File`]: https://doc.rust-lang.org/std/fs/struct.File.html
[`foreign_links`]: https://docs.rs/error-chain/*/error_chain/#foreign-links
[`gen_ascii_chars`]: https://docs.rs/rand/0.3.18/rand/trait.Rng.html#method.gen_ascii_chars

This comment has been minimized.

@budziq

budziq Dec 6, 2017

Collaborator

lets use wildcard version numbers

let s: String = thread_rng().gen_ascii_chars().take(30).collect();
println!("{}", s);
let alphanum_bytestring =

This comment has been minimized.

@budziq

budziq Dec 6, 2017

Collaborator

I'd suggest implementing an iterator type like the AsciiGenerator instead that would take an rng in a constructor and for added merrit a "charset" (a hardcoded bytestr for our purposes without anych dynamic generation with ranges and chain) as an argument.

Then it would be used almost identically as the gen_ascii_chars

let mut rand_bytevec: Vec<u8> = Vec::new();
for _ in 0..30 {

This comment has been minimized.

@budziq

budziq Dec 6, 2017

Collaborator

If the iterator approach would be used, pretty much all of this code would be not needed.

@@ -8,6 +8,7 @@
| [Generate random numbers within a range][ex-rand-range] | [![rand-badge]][rand] | [![cat-science-badge]][cat-science] |
| [Generate random numbers with given distribution][ex-rand-dist] | [![rand-badge]][rand] | [![cat-science-badge]][cat-science] |
| [Generate random values of a custom type][ex-rand-custom] | [![rand-badge]][rand] | [![cat-science-badge]][cat-science] |
| [Create random passwords from a set of allowed characters][ex-rand-passwd] | [![rand-badge]][rand] | |

This comment has been minimized.

@budziq

budziq Dec 6, 2017

Collaborator

there should be a category here

@@ -26,6 +26,7 @@ community. It needs and welcomes help. For details see
| [Generate random numbers within a range][ex-rand-range] | [![rand-badge]][rand] | [![cat-science-badge]][cat-science] |
| [Generate random numbers with given distribution][ex-rand-dist] | [![rand-badge]][rand] | [![cat-science-badge]][cat-science] |
| [Generate random values of a custom type][ex-rand-custom] | [![rand-badge]][rand] | [![cat-science-badge]][cat-science] |
| [Create random passwords from a set of allowed characters][ex-rand-passwd] | [![rand-badge]][rand] | |

This comment has been minimized.

@budziq

budziq Dec 6, 2017

Collaborator

there should be a category here

String::from_utf8((b'a'..b'z')
.chain(b'A'..b'Z')
.chain(b'0'..b'9')
//.chain([b'!', b'?', b'%'])

This comment has been minimized.

@budziq

budziq Dec 6, 2017

Collaborator

It does not work because ranges give you u8 while iterating over slice gives you &u8 so the solution (if we wanted to go with this approach and we do not ;)) would be to clone the iterator contents.
.chain([b'!', b'?', b'%'].iter().cloned()) or even better .chain(b"!?%".iter().cloned()).

On the other hand I would suggest to just go with bytestring literal

const CHARSET: &[u8] =  b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
              abcdefghijklmnopqrstuvwxyz\
              0123456789)(*&^%$#@!~";

@anna-liao anna-liao force-pushed the anna-liao:issue287 branch from 69e730e to e845d5c Dec 6, 2017

@anna-liao

This comment has been minimized.

Copy link
Contributor Author

anna-liao commented Dec 6, 2017

@budziq Thanks for the review. I revised the PR based on your suggestions. With the choose example, if I manually compose the vec it seems to work, but I am having issues writing an interator like for AsciiGenerator. Can you provide some guidance on how to write CustomGenerator to work properly?

@budziq
Copy link
Collaborator

budziq left a comment

@anna-liao few suggestions below :)

use rand::{thread_rng, Rng};
fn main() {
let s: String = thread_rng().gen_ascii_chars().take(30).collect();

This comment has been minimized.

@budziq

budziq Dec 6, 2017

Collaborator

lets change the variable name to a more descriptive one.

let mut s = String::new();
for _ in 0..30 {
s.push(*thread_rng().choose(CHARSET).unwrap() as char);

This comment has been minimized.

@budziq

budziq Dec 6, 2017

Collaborator

instead of for loop you can use a relatively simpler functional appproach without a need for manual string append. Also obtaining a thread_rng might be relatively costly so lets reuse one instance througth the generation process.

    let mut rng = thread_rng();
    let password: String = (0..30)
        .map(|_| *rng.choose(CHARSET).unwrap() as char)
        .collect();

This version might be more readable and concise than separate type approach with CustomGenerator so I'll leave it up to you to chose one.

}
}
fn gen_rand_chars<'a>(&'a mut self) -> CustomGenerator<'a, Self> where Self: Sized {

This comment has been minimized.

@budziq

budziq Dec 6, 2017

Collaborator

I guess that you are trying to emulate the gen_ascii_chars method here but it is not possible to extend the Rng in such way. All we can do is prepare a standard constructor that will take an rng and a charset.

Some along the lines of

impl<'a, R> CustomGenerator<'a, R> {
    fn new(rng: R, charset: &'a [u8]) -> Result<Self> {
        if charset.len() > 0 {
            Some(CustomGenerator {
                rng: rng,
                charset: charset,
            })
        } else {
                 Err(ErrorKind::InvalidCharset.into())
        }
    }
}
use rand::{thread_rng, Rng};
struct CustomGenerator<'a, R:'a> {
rng: &'a mut R,

This comment has been minimized.

@budziq

budziq Dec 6, 2017

Collaborator

I'd probably go with owing the rng to make the lifetimes simpler. But I'll leave it up to you :)
We might go with having a refference to some outsude charset

@anna-liao anna-liao force-pushed the anna-liao:issue287 branch 3 times, most recently from 8f8eef6 to 6013912 Dec 7, 2017

@anna-liao

This comment has been minimized.

Copy link
Contributor Author

anna-liao commented Dec 7, 2017

@budziq Thanks for the help! I have learned more about idiomatic Rust syntax. I decided to stick with the non-generator case for user-specified charset. I couldn't get the generator case working. I think the generator case would be good as an additional example?

@budziq
Copy link
Collaborator

budziq left a comment

Minor suggestions in a review

I couldn't get the generator case working.

No problem it is a bit verbose anyway

let mut rng = thread_rng();
let password: String = (0..30)
.map(|_| *rng.choose(CHARSET).unwrap() as char)

This comment has been minimized.

@budziq

budziq Dec 8, 2017

Collaborator

we generally avoid unwrap in the examples, we should either document why the unwrap will be always ok as long as the CHARSET is nonempty or provide a generalized solution like:

    let password: Option<String> = (0..30)
        .map(|_| Some(*rng.choose(CHARSET)? as char))
        .collect();

    println!("{:?}", password);

This comment has been minimized.

@anna-liao

anna-liao Dec 12, 2017

Author Contributor

@budziq Thanks for showing me how to implement the generator case! I have updated the PR as suggested to remove the unwrap() and print an Option instead.

@anna-liao anna-liao force-pushed the anna-liao:issue287 branch from 6013912 to c1b8a31 Dec 12, 2017

@budziq budziq merged commit 5da134d into rust-lang-nursery:master Dec 12, 2017

2 checks passed

continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details

@anna-liao anna-liao deleted the anna-liao:issue287 branch Dec 12, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.