Misc: Syntax, Crates, std
CIS 198 Lecture 7
const
1. const PI: f32 = 3.1419;
- Defines constants that live for the duration of the program.
- Must annotate the type!
- Constants “live” for the duration of the program.
- Think of them as being inlined every time they’re used.
- No guarantee that multiple references to the same constant are the same.
static
1. static PI: f32 = 3.1419;
- As above: must annotate type.
- Typical global variable with fixed memory address.
- All references to static variables has the
'staticlifetime, because statics live as long as the program. unsafeto mutate.
1. let life_of_pi: &'static f32 = Π
- String literals are references (with lifetime
'static) tostatic strs.
static
1. static mut counter: i32 = 0;
- You can create mutable static variables, but you can only mutate them inside
unsafeblocks. - Rust forces you to declare when you’re doing things that are… ~~morally questionable~~ potentially going to crash your program.
Modules & Crates
Modules
- We’ve seen these in the homework, but not talked about them.
- Everything in Rust is module-scoped: if it’s not pub, it’s only accessible from within the same module.
- Modules can be defined within one file:
1. mod english {
2. pub mod greetings {
3. }
4. pub mod farewells {
5. }
6. }
8. mod japanese {
9. pub mod greetings {
10. }
11. pub mod farewells {
12. }
13. }
Reference: TRPL 4.25
Modules
1. mod english {
2. pub mod greetings { /* ... */ }
3. }
- Modules can be defined as files instead:
lib.rs:
1. mod english;
- english.rs:
1. pub mod greetings { /* ... */ }
Modules
1. mod english {
2. pub mod greetings { /* ... */ }
3. }
- Modules can also be defined as directories:
lib.rs:
1. mod english;
- english/
- mod.rs:
```
1. pub mod greetings;
```
-
greetings.rs:1. /* ... */
Namespacing
- When accessing a member of a module, by default, namespaces are relative to the current module:
1. mod one {
2. mod two { pub fn foo() {} }
3. fn bar() {
4. two::foo()
5. }
6. }
- But it can be made absolute with a leading
::operator:
1. mod one {
2. mod two { pub fn foo() {} }
3. fn bar() {
4. ::one::two::foo()
5. }
6. }
useing Modules
usehas the opposite rules.usedirectives are absolute by default:
1. use english::greetings;
- But can be relative to the current module:
1. // english/mod.rs
2. use self::greetings;
3. use super::japanese;
pub usecan be used to re-export other items:
1. // default_language.rs
3. #[cfg(english)]
4. pub use english::*;
6. #[cfg(japanese)]
7. pub use japanese::*;
Using External Crates
- For external crates, use
extern crateinstead ofmod.
1. extern crate rand;
3. use rand::Rng;
Making Your Own Crate
- We’ve been writing lib crates - but how do we export from them?
- Anything marked
pubin the root module (lib.rs) is exported:
1. pub mod english;
- Easy!
Using Your Own Crate
- Now, you can use your own crate from Cargo:
1. [dependencies]
2. myfoo = { git = "https://github.com/me/foo-rs" }
3. mybar = { path = "../rust-bar" }
- Or:
1. [dependencies.myfoo]
2. git = "https://github.com/me/foo-rs"
- And use them:
1. extern crate myfoo;
3. use myfoo::english;
Cargo: you got your bins in my lib
- We’ve seen both lib and bin (executable) crates in homework
- Executable-only crates don’t export any importable crates.
- But this isn’t really a distinction!
- Cargo allows both
:/src/lib.rsand:/src/main.rs. - Cargo will also build
:/src/bin/*.rsas executables. - Examples go in
:/examples/*.rs. - Built by
cargo test(to ensure examples always build). - Can be called with
cargo run --example foo. - Integration (non-unit) tests go in
:/tests/*.rs. - Benchmarks go in
:/benches/*.rs.
Cargo: Features
- Features of a crate can be toggled at build time:
cargo build --features using-html9
1. [package]
2. name = "myfacebumblr"
4. [features]
5. # Enable default dependencies: require web-vortal *feature*
6. default = ["web-vortal"]
8. # Extra feature; now we can use #[cfg(feature = "web-vortal")]
9. web-vortal = []
11. # Also require h9rbs-js *crate* with its commodore64 feature.
12. using-html9 = ["h9rbs-js/commodore64"]
14. [dependencies]
15. # Optional dependency can be enabled by either:
16. # (a) feature dependencies or (b) extern crate h9rbs_js.
17. h9rbs-js = { optional = "true" }
Cargo: Build Scripts
- Sometimes, you need more than what Cargo can provide.
- For this, we have build scripts!
- Of course, they’re written in Rust.
1. [package]
2. build = "build.rs"
- Now,
cargo buildwill compile and run:/build.rsfirst.
Cargo: The Rabbit Hole
- Cargo has a lot of features. If you’re interested, check them out in the Cargo manifest format documentation.
Attributes
- Ways to pass information to the compiler.
#[test]is an attribute that annotates a function as a test.#[test]annotates the next block;#![test]annotates the surrounding block.
1. #[test]
2. fn midterm1() {
3. // ...
4. }
6. fn midterm2() {
7. #![test]
8. // ...
9. }
Attributes
- Use attributes to…
#![no_std]disable the standard library.#[derive(Debug)]auto-derive traits.#[inline(always)]give compiler behavior hints.#[allow(missing_docs)]disable compiler warnings for certain lints.#![crate_type = "lib"]provide crate metadata.#![feature(box_syntax)]enable unstable syntax.#[cfg(target_os = "linux")]define conditional compilation.- And many more!
Rust Code Style
Rust Code Style
- A style guide is being drafted as part of the Rust docs.
- The main reason for many of the rules is to prevent pointless arguments about things like spaces and braces.
- If you contribute to an open-source Rust project, it will probably be expected that you follow these rules.
- The rustfmt project is an automatic code formatter.
Spaces
- Lines must not exceed 99 characters.
- Use 4 spaces for indentation, not tabs.
- No trailing whitespace at the end of lines or files.
- Use spaces around binary operators:
x + y. - Put spaces after, but not before, commas and colons:
x: i32. - When line-wrapping function parameters, they should align.
1. fn frobnicate(a: Bar, b: Bar,
2. c: Bar, d: Bar)
3. -> Bar {
4. }
Braces
- Opening braces always go on the same line.
- Match arms get braces, except for single-line expressions.
returnstatements get semicolons.- Trailing commas (in structs, matches, etc.) should be included if the closing delimiter is on a separate line.
Capitalization & Naming
- You may have seen built-in lints on how to spell identifiers.
CamelCase: types, traits.lowerCamelCase: not used.snake_case: crates, modules, functions, methods, variables.SCREAMING_SNAKE_CASE: static variables and constants.T(single capital letter): type parameters.'a(tick + short lowercase name): lifetime parameters.- Constructors and conversions should be worded:
new,new_with_stuff: constructors.from_foo: conversion constructors.as_foo: free non-consuming conversion.to_foo: expensive non-consuming conversion.into_foo: consuming conversion.
Advanced format!ing
- The
?means debug-print. But what goes before the:part? - A positional parameter! An index into the argument list.
1. println!("{2} {} {} {0} {} {}", 0, 1, 2, 3) // ==> "2 0 1 0 2 3"
- Among the specifiers with no positional parameter, they implicitly
count up:
{0} {1} {2} .... - There are also named parameters:
1. format!("{name} {}", 1, name = 2); // ==> "2 1"
format! Specifiers
- We’ve been printing stuff out with
println!("{:?}", bst); - There are more format specifiers than just
{}and{:?}. - These all call traits in
std::fmt:
| Spec. | Trait | Spec. | Trait | Spec. | Trait |
|---|---|---|---|---|---|
{} |
Display | {:?} |
Debug | {:o} |
Octal |
{:x} |
LowerHex | {:X} |
UpperHex | {:p} |
Pointer |
{:b} |
Binary | {:e} |
LowerExp | {:E} |
UpperExp |
format! Specifiers
- There are tons of options for each of these format specifiers.
- Examples:
{:04}->0010: padding'{:^4}'->' 10 ': alignment (centering)#indicates an “alternate” print format:{:#X}->0xA: including0x{:#?}: Pretty-prints objects:
1. A {
2. x: 5,
3. b: B {
4. y: 4
5. }
6. }
- Complete reference: std::fmt
Operators
- Operators are evaluated left-to-right, in the following order:
- Unary operators:
!-*&&mut ascasting*/%multiplicative arithmetic+-additive arithmetic<<>>shift arithmetic&bitwise and^bitwise xor|bitwise or==!=<><=>=logical comparison&&logical and||logical or=..assignment and ranges- Also:
call(),index[]
Operator Overloading
- Okay, same old, same old. We can customize these!
- Rust defines these - surprise! - using traits, in
std::ops. Neg,Not,Deref,DerefMutMul,Div,ModAdd,SubShl,ShrBitAndBitXorBitOrEq,PartialEq,Ord,PartialOrdAndOr- Also:
Fn,FnMut,FnOnce,Index,IndexMut,Drop
From One Type Into Another
- Casting (
as) cannot be overloaded - instead, we useFromandInto. trait From<T> { fn from(T) -> Self; }, called likeY::from(x).trait Into<T> { fn into(self) -> T; }, called likex.into().- If you implement
From,Intowill be automatically implemented. - So you should prefer implementing
From.
1. struct A(Vec<i32>);
2. impl From<Vec<i32>> for A {
3. fn from(v: Vec<i32>) -> Self {
4. A(v)
5. }
6. }
From One Type Into Another
- But sometimes, for various reasons, implementing
Fromisn’t possible - onlyInto.
1. struct A(Vec<i32>);
3. impl From<A> for Vec<i32> { // error: private type A in
4. fn from(a: A) -> Self { // exported type signature.
5. let A(v) = a; v // (This impl is exported because
6. } // both the trait (From) and the type
7. } // (Vec) are visible from outside.)
9. impl Into<Vec<i32>> for A {
10. fn into(self) -> Vec<i32> {
11. let A(v) = self; v
12. }
13. }
Making References
Borrow/BorrowMut: “a trait for borrowing data.”¹
1. trait Borrow<Borrowed> { fn borrow(&self) -> &Borrowed; }
AsRef/AsMut: “a cheap, reference-to-reference conversion.”²
1. trait AsRef<T> { fn as_ref(&self) -> &T; }
- So… they’re exactly the same?
Making References
- No! While the have the same definition,
Borrowcarries additional connotations: - “If you are implementing Borrow and both Self and Borrowed implement Hash, Eq, and/or Ord, they must produce the same result.”¹ ²
- Borrow has a blanket implementation:
impl<T> Borrow<T> for T: you can always convertTto&T.AsRefactually has its own blanket implementation:impl<'a, T, U> AsRef<U> for &'a T where T: AsRef<U>- For all
T, ifTimplementsAsRef,&Talso implementsAsRef. - All this means you usually want to implement
AsRef.
