锘??xml version="1.0" encoding="utf-8" standalone="yes"?>国产91色综合久久免费,久久男人AV资源网站,精品久久久无码21p发布http://www.shnenglu.com/jinq0123/archive/2023/01/01/229602.html閲戝簡閲戝簡Sun, 01 Jan 2023 02:37:00 GMThttp://www.shnenglu.com/jinq0123/archive/2023/01/01/229602.htmlhttp://www.shnenglu.com/jinq0123/comments/229602.htmlhttp://www.shnenglu.com/jinq0123/archive/2023/01/01/229602.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/229602.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/229602.htmlFix navmesh countour
(Jin Qing's Column, Jan., 2023)
After changing some parameters of watershed region partition algorithm,
my test mesh generated an odd region shape, which caused a wrong contour.
Wrong contour:
Wrong navmesh:
There are 2 regions, the big region with red contour forms a C shape,
which is closed at one point.
The error is caused by the closing point, which should be a vertex in the simplified contour
of the small blue region but not.
In simplifyContour(), the closing point is incorrectly regarded as the edge point between 2 regions,
and is skipped, but it is accurately a vertex among 3 regions and must be a contour vertex.
To fix the problem, each vertex of the raw contour should count the regions around the vertex.
If the regions count is 3 or 4, then it must be a contour vertex of the simplified contour.
Fixed contour:
Code:
```
// Returns the regions count of the contour vertex's 4 cells.
// The contour vertex is the front-right of the current span.
// This vertex must be the simplified contour's vertex.
// The non-region(0) is count as a region.
//
// reg is 4 region IDs of 4 cells in clockwise order: 0-current, 1-dir, 2-diagonal, 3-dirp
//
// Diagonal cells are not connected, so these 4 cells have regions count 4,
//   because region a and b are separated from each other:
//   ```
//   a|b
//   -+-
//   b|a
// ```
size_t getRegionsCount(const rcRegionId reg[4])
{
size_t diffCount = 0;
for (size_t i = 0, j = 3; i < 4; j = i++)
{
if (reg[i] != reg[j])
++diffCount;
}
assert(diffCount <= 4);
assert(diffCount != 1);
if (0 == diffCount)
return 1;
return diffCount;
}
void testGetRegionsCount()
{
assert(1 == getRegionsCount4(1, 1, 1, 1));
assert(2 == getRegionsCount4(1, 0, 0, 0));
assert(2 == getRegionsCount4(1, 1, 0, 0));
assert(3 == getRegionsCount4(0, 1, 2, 0));
assert(4 == getRegionsCount4(0, 1, 2, 3));
assert(4 == getRegionsCount4(1, 0, 1, 0));
assert(4 == getRegionsCount4(1, 0, 2, 0));
}
```

test.obj: /Files/jinq0123/test.zip


閲戝簡 2023-01-01 10:37 鍙戣〃璇勮
]]>
How are dtLinks created in NavMeshhttp://www.shnenglu.com/jinq0123/archive/2022/11/18/229525.html閲戝簡閲戝簡Fri, 18 Nov 2022 02:03:00 GMThttp://www.shnenglu.com/jinq0123/archive/2022/11/18/229525.htmlhttp://www.shnenglu.com/jinq0123/comments/229525.htmlhttp://www.shnenglu.com/jinq0123/archive/2022/11/18/229525.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/229525.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/229525.htmlHow are dtLinks created in NavMesh

(Jin Qing's Column, Nov., 2022)

dtLink defines a link between 2 polygons. It is an internal data structure.

A dtLink is owned by its source polygon.

The links are used in findPath(), to search all the neighbor polygons, from the current best polygon.

dtLink has 2 required fields:

  • neighbor: the neighbor polygon reference that is linked to.
  • edge: index of the polygon edge that owns this link. It is used in getPortalPoints() and raycast().

If the link is a boundary link, which links to other tile, then it has more fields:

  • side: defines on which side the link is. 8 sides. It is used in findPath() and raycast().
  • bmin: defines the minimum sub-edge area.
  • bmax: defines the maximum sub-edge area. bmin and bmax are used in portal finding.

Links are not saved to navmesh file. They are created on navmesh loading in addTile().

dtNavMesh::addTile() add a tile to the navmesh. A tile is a square block of the map.

The main job of dtNavMesh::addTile() is to create links inside this tile and between this tile and other tiles. Actually a block indexed by (x, y) of constant size has many layers for multi-floor map. A dtMeshTile is actually a layer with an index of (x, y, layer).

5 steps to create all links:

connectIntLinks() iterates all the polygons in the tile and create links for them by the help of neighbors information of the polygons.

2. Base off-mesh connections

baseOffMeshLinks() bases off-mesh connections to their starting polygons and connect connections inside the tile.

Off-mesh connection is between 2 points which are inside a tile or between 2 adjacent tiles.

For each off-mesh connection, there creates a specific polygon consisting of 2 vertices. See DT_POLYTYPE_OFFMESH_CONNECTION.

baseOffMeshLinks() creates 2 links for each off-mesh connection:

  • From the off-mesh connection polygon to the source polygon
  • From the source polygon to the off-mesh connection polygon

The destinaton polygon of the off-mesh connection is skipped here.

connectExtOffMeshLinks() with the source and target tile be this tile.

connectExtOffMeshLinks() searches off-mesh connections that are from this tile to the target tile by checking the side direction of the connection.

It creates a link from off-mesh connection polygon to the target tile. For bidirectional connection, it also creates a link from the target polygon to the off-mesh connection polygon.

So for each off-mesh connection of this tile, 3 or 4 links are created by baseOffMeshLinks() and connectExtOffMeshLinks(), one on the source polygon, one on the destination polygon and 1/2 on the off-mesh connection polygon.

4. Connect with layers in current tile

For each layer other than this layer in this tile:

  • Create links from this layer to other layer
  • Create links from other layer to this layer
  • Create links of the off-mesh connection from this layer to other layer
  • Create links of the off-mesh connection from other layer to this layer

5. Connect with neighbour tiles

Check 9 neighbor tiles' layers, and create links just like previous step:

  • Create links from this layer to neighnor layer
  • Create links from neighnor layer to this layer
  • Create links of the off-mesh connection from this layer to neighnor layer
  • Create links of the off-mesh connection from neighnor layer to this layer

By now, for every off-mesh connection of all the loaded tiles, 3/4 links are created.

Reference

  • https://blog.csdn.net/icebergliu1234/article/details/80322392


閲戝簡 2022-11-18 10:03 鍙戣〃璇勮
]]>
C++ parameter passing ruleshttp://www.shnenglu.com/jinq0123/archive/2022/10/29/229466.html閲戝簡閲戝簡Sat, 29 Oct 2022 03:01:00 GMThttp://www.shnenglu.com/jinq0123/archive/2022/10/29/229466.htmlhttp://www.shnenglu.com/jinq0123/comments/229466.htmlhttp://www.shnenglu.com/jinq0123/archive/2022/10/29/229466.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/229466.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/229466.htmlC++ parameter passing rules

From: https://www.modernescpp.com/index.php/c-core-guidelines-how-to-pass-function-parameters

Parameter passing expression rules:

  • F.15: Prefer simple and conventional ways of passing information
  • F.16: For “in” parameters, pass cheaply-copied types by value and others by reference to const
  • F.17: For “in-out” parameters, pass by reference to non-const
  • F.18: For “consume” parameters, pass by X&& and std::move the parameter
  • F.19: For “forward” parameters, pass by TP&& and only std::forward the parameter
  • F.20: For “out” output values, prefer return values to output parameters
  • F.21: To return multiple “out” values, prefer returning a tuple or struct
  • F.60: Prefer T* over T& when “no argument” is a valid option


閲戝簡 2022-10-29 11:01 鍙戣〃璇勮
]]>
Implementation of user data in cursive::Cursivehttp://www.shnenglu.com/jinq0123/archive/2022/10/02/229436.html閲戝簡閲戝簡Sun, 02 Oct 2022 08:38:00 GMThttp://www.shnenglu.com/jinq0123/archive/2022/10/02/229436.htmlhttp://www.shnenglu.com/jinq0123/comments/229436.htmlhttp://www.shnenglu.com/jinq0123/archive/2022/10/02/229436.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/229436.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/229436.htmlImplementation of user data in cursive::Cursive

(Jin Qing's Column, Oct., 2022)

cursive::Cursive can store any user data:

pub struct Cursive { ... // User-provided data. user_data: Box<dyn Any>, } impl Cursive { ... pub fn set_user_data<T: Any>(&mut self, user_data: T) { self.user_data = Box::new(user_data); } pub fn user_data<T: Any>(&mut self) -> Option<&mut T> { self.user_data.downcast_mut() } pub fn take_user_data<T: Any>(&mut self) -> Option<T> { ... } pub fn with_user_data<F, T, R>(&mut self, f: F) -> Option<R> where F: FnOnce(&mut T) -> R, T: Any, { self.user_data().map(f) }

Example:

let mut siv = cursive_core::Cursive::new(); // Start with a simple `Vec<i32>` as user data. siv.set_user_data(vec![1i32, 2, 3]); assert_eq!(siv.user_data::<Vec<i32>>(), Some(&mut vec![1i32, 2, 3])); // Let's mutate the data a bit. siv.with_user_data(|numbers: &mut Vec<i32>| numbers.push(4)); // If mutable reference is not enough, we can take the data by value. let data: Vec<i32> = siv.take_user_data().unwrap(); assert_eq!(data, vec![1i32, 2, 3, 4]); // At this point the user data was removed and is no longer available. assert_eq!(siv.user_data::<Vec<i32>>(), None);


閲戝簡 2022-10-02 16:38 鍙戣〃璇勮
]]>
Naming Conventions for Accessorshttp://www.shnenglu.com/jinq0123/archive/2022/09/22/229427.html閲戝簡閲戝簡Thu, 22 Sep 2022 08:02:00 GMThttp://www.shnenglu.com/jinq0123/archive/2022/09/22/229427.htmlhttp://www.shnenglu.com/jinq0123/comments/229427.htmlhttp://www.shnenglu.com/jinq0123/archive/2022/09/22/229427.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/229427.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/229427.htmlNaming Conventions for Accessors

from: C++ Programming Guidelines (cginternals.github.io)

When naming accessors within classes, non-trivial getters and queries, i.e., those that perform calculations, you should prepended get. All other getters have no prefix and setters have the set prefix.

class Object
{
public:
  int radius() const;
  void setRadius(int value);

  int getPerimeter();

private:
  int m_radius;
};

int Object::radius() const
{
    return m_radius;
}

void Object::setRadius(int value)
{
    m_radius = value;
}

int Object::getPerimeter()
{
    return 2 * pi * m_radius;
}


閲戝簡 2022-09-22 16:02 鍙戣〃璇勮
]]>
Rust sometimes needs manual type annotationhttp://www.shnenglu.com/jinq0123/archive/2022/04/29/229301.html閲戝簡閲戝簡Fri, 29 Apr 2022 02:14:00 GMThttp://www.shnenglu.com/jinq0123/archive/2022/04/29/229301.htmlhttp://www.shnenglu.com/jinq0123/comments/229301.htmlhttp://www.shnenglu.com/jinq0123/archive/2022/04/29/229301.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/229301.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/229301.htmlRust sometimes needs manual type annotation

(Jin Qing's Column, Apr., 2022)

This code compiles error:

      trait MyCallback: FnMut(&u32) -> () { }
impl<F: FnMut(&u32) -> ()> MyCallback for F { }
fn process_data(mut f: impl MyCallback) -> () {
    f(&0)
}
fn process_data_2(mut f: impl FnMut(&u32) -> ()) -> () {
    f(&0)
}
fn main() {
    // Doesn't compile
    process_data(|_| ());
    // Compiles
    process_data_2(|_| ());
}

    
      expected type `for<'r> FnMut<(&'r u32,)>`
              found type `FnMut<(&u32,)>`

    

Fix:

          process_data(|_: &_| ());

    

See: https://stackoverflow.com/questions/61671460/rust-type-mismatch-resolving-forr-with-closure-trait-alias-argument

Also see: https://github.com/rust-lang/rust/issues/58639

Sometimes, Rust needs a type annotation in closure.



閲戝簡 2022-04-29 10:14 鍙戣〃璇勮
]]>
Fix recastnavigation contour errorhttp://www.shnenglu.com/jinq0123/archive/2022/04/28/229299.html閲戝簡閲戝簡Thu, 28 Apr 2022 05:34:00 GMThttp://www.shnenglu.com/jinq0123/archive/2022/04/28/229299.htmlhttp://www.shnenglu.com/jinq0123/comments/229299.htmlhttp://www.shnenglu.com/jinq0123/archive/2022/04/28/229299.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/229299.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/229299.htmlix recastnavigation contour error

(Jin Qing's Column, Apr., 2022)

Using navmesh generated by recastnavigation, we found the agent will collide into the wall when it is walking along the navmesh path.

After some inspection, we found the reason is that at the collision position, the position on the path is simply too close to the wall, which is less than the agent radius.

If the agent radius is zero, the navmesh contour may inside the wall.

Displaying the raw contour and contour, we can see the raw contour which is jigsaw shaped is right, but the simplied contour uses a straight line to replace the jigsaw edge, and causes the error.


The parameter "Max Edge Error" controls how the contour is simplified, which default is 1.3, which means a vertex can deviate from the final contour in the range of 1.3 times of cell size.

We hope the error is on only one side of the contour instead of on both sides. In other words, the contour should within the raw contour.

walkContour() finds a raw contour which is a polygon. simplifyContour() simplifies the raw contour resulting a simpler polygon.

The steps of simplifyContour():

  1. Find lower-left and upper-right vertices of the contour to form the initial simplified polygon.
  2. For each edge of the simplified polygon
    1. For each point between 2 points of the edge
      1. Find the point that is the most distant from the edge
    2. If the distance is larger than the max error, add the point to the simplified polygon
  3. Split too long edges.

We make these modifications:

  • The distance is signed
    • If the point is outside the walkable area, just make sure it is less than the max error
    • If the point is inside the walkable area
      • Add it to the simplified polygon to make sure the final contour is within the raw area

There are 2 kinds of contour:

  • Outline of a walkable area
  • Hole of a walkable area

We should decide the kind of the contour before simplifyContour(). If the start cell (x, y) is in the raw contour polygon, then it is an outline not a hole contour. Because (x, y) may be one of the vetex of the polygon, we use (x + 0.5, y + 0.5) instead, which is the center of the cell.

The result will be like this:


Compare with the orignal contour:



閲戝簡 2022-04-28 13:34 鍙戣〃璇勮
]]>
How to work around rustup-init failurehttp://www.shnenglu.com/jinq0123/archive/2022/04/24/229295.html閲戝簡閲戝簡Sun, 24 Apr 2022 06:09:00 GMThttp://www.shnenglu.com/jinq0123/archive/2022/04/24/229295.htmlhttp://www.shnenglu.com/jinq0123/comments/229295.htmlhttp://www.shnenglu.com/jinq0123/archive/2022/04/24/229295.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/229295.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/229295.htmlHow to work around rustup-init failure

(Jin Qing's Column, Mar., 2022)

rustup-init.exe may fail if some anti-virus software is running with realtime protection.

The error message is like this after many retries:

error: could not rename component file from 'C:\.../rust/html' to 'C:\.../rust/html'

See: https://github.com/rust-lang/rustup/issues/1912

And it is not possible to stop the anti-virus for a corporate laptop.

There is a way to workaround this. Here are the steps:

  1. Run rustup-init until it begins to retry renaming
  2. Kill rustup-init when it is retrying
    • rustup will be installed before the failure
    • do not let rustup-init do the cleanup after the final failure
  3. Run rustup update to install all.
  4. Run rustup-init again to setup others.

rustup update will report that rustc was not found:

C:\Users\jinqing01>rustup update
info: syncing channel updates for 'stable-x86_64-pc-windows-msvc'
...
info: removing previous version of component 'rustc'
warning: during uninstall component rustc was not found
...
info: installing component 'rustc'
...  
info: checking for self-updates
  stable-x86_64-pc-windows-msvc updated - rustc 1.59.0 (9d1b2106e 2022-02-23) (from (rustc does not exist))
info: cleaning up downloads & tmp directories

Rerun rustup-init to setup the toolchain:

C:\Users\jinqing01>cargo --version
error: no override and no default toolchain set
C:\Users\jinqing01>d:
D:\>cd tool
D:\Tool>rustup-init.exe
Welcome to Rust!
This will download and install the official compiler for the Rust
programming language, and its package manager, Cargo.
...
Rust is installed now. Great!
Press the Enter key to continue.
D:\Tool>cargo --version
cargo 1.59.0 (49d8809dc 2022-02-10)



閲戝簡 2022-04-24 14:09 鍙戣〃璇勮
]]>
Closure as the function parameterhttp://www.shnenglu.com/jinq0123/archive/2022/04/24/229294.html閲戝簡閲戝簡Sun, 24 Apr 2022 06:06:00 GMThttp://www.shnenglu.com/jinq0123/archive/2022/04/24/229294.htmlhttp://www.shnenglu.com/jinq0123/comments/229294.htmlhttp://www.shnenglu.com/jinq0123/archive/2022/04/24/229294.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/229294.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/229294.htmlClosure as the function parameter

(Jin Qing's Column, Mar., 2022)

It is best to let the function take a closure trait as the parameter instead of a function pointer.

      fn foo(f: fn()) {
    f()
}
fn main() {
    foo(|| println!("hello"));
    let a = 123;
    foo(move || println!("{}", a))
}

    

compiles error:

      error[E0308]: mismatched types
 --> src/main.rs:9:9
  |
9 |     foo(move || println!("{}", a))
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure
  |
  = note: expected fn pointer `fn()`
                found closure `[closure@src/main.rs:9:9: 9:34]`
note: closures can only be coerced to `fn` types if they do not capture any variables
 --> src/main.rs:9:32
  |
9 |     foo(move || println!("{}", a))
  |                                ^ `a` captured here
For more information about this error, try `rustc --explain E0308`.

    

https://doc.rust-lang.org/book/ch19-05-advanced-functions-and-closures.html

Function pointers implement all three of the closure traits (Fn, FnMut, and FnOnce), so you can always pass a function pointer as an argument for a function that expects a closure. It’s best to write functions using a generic type and one of the closure traits so your functions can accept either functions or closures.



閲戝簡 2022-04-24 14:06 鍙戣〃璇勮
]]>
Deadlock detection must not run as tokio taskhttp://www.shnenglu.com/jinq0123/archive/2022/04/24/229293.html閲戝簡閲戝簡Sun, 24 Apr 2022 06:04:00 GMThttp://www.shnenglu.com/jinq0123/archive/2022/04/24/229293.htmlhttp://www.shnenglu.com/jinq0123/comments/229293.htmlhttp://www.shnenglu.com/jinq0123/archive/2022/04/24/229293.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/229293.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/229293.htmldeadlock detection must not run as tokio task

(Jin Qing's Column, Feb., 2022)

parking_lot has an experimental feature: deadlock_detection.

See: https://amanieu.github.io/parking_lot/parking_lot/deadlock/index.html

use std::thread;
use std::time::Duration;
use parking_lot::deadlock;
// Create a background thread which checks for deadlocks every 10s
thread::spawn(move || {
loop {
thread::sleep(Duration::from_secs(10));
let deadlocks = deadlock::check_deadlock();
if deadlocks.is_empty() {
continue;
}
println!("{} deadlocks detected", deadlocks.len());
for (i, threads) in deadlocks.iter().enumerate() {
println!("Deadlock #{}", i);
for t in threads {
println!("Thread Id {:#?}", t.thread_id());
println!("{:#?}", t.backtrace());
}
}
}
});

The output is like this:

1 deadlocks detected
Deadlock #0
Thread Id 16072
0: 0x7ff985cb659d - backtrace::backtrace::dbghelp::trace
at d:\Users\jinqing\.cargo\registry\src\github.com-1ecc6299db9ec823\backtrace-0.3.63\src\backtrace\dbghelp.rs:98
...
13: 0x7ff985ae92f3 - lock_api::rwlock::RwLock<parking_lot::raw_rwlock::RawRwLock,cgc::scene_template::SceneTemplate>::read<parking_lot::raw_rwlock::RawRwLock,cgc::scene_template::SceneTemplate>
at d:\Users\jinqing\.cargo\registry\src\github.com-1ecc6299db9ec823\lock_api-0.4.6\src\rwlock.rs:448
14: 0x7ff985aeadf3 - cgc::scene::SceneData::check_body_collide
at E:\gitlab\yserver\gc\src\scene.rs:99
...
81: 0x7ff9f29b7034 - BaseThreadInitThunk
82: 0x7ff9f2b02651 - RtlUserThreadStart

But the deadlock detection thread can not be changed to a tokio task, because if deadlock happens, all tasks may be blocked, including the deadlock detection, causing no deadlock error output.

In the following example, if the number of the deadlock tasks is larger than the thread number of tokio runtime, all tasks will be blocked.

use std::{thread, time::Duration};
use parking_lot::RwLock;
use tokio::time;
#[tokio::main]
async fn main() {
tokio::spawn(async move {
for i in 0..999999 {
println!("{}", i);
time::sleep(Duration::from_secs(1)).await;
}
});
const MAX: i32 = 100;
for _ in 0..MAX {
tokio::spawn(async move {
{
// DEADLOCK!
let a = RwLock::new(());
let _g1 = a.write();
let _g2 = a.write();
}
time::sleep(Duration::from_secs(9999)).await;
});
}
println!("Hello, world!");
thread::sleep(Duration::from_secs(10));
}

The output is:

0
Hello, world!
_

If no deadlock, or the number of deadlock tasks is small, the output should be:

0
Hello, world!
1
2
3
...



閲戝簡 2022-04-24 14:04 鍙戣〃璇勮
]]>
TortoiseGit is OK but GitExtensions failshttp://www.shnenglu.com/jinq0123/archive/2022/04/24/229292.html閲戝簡閲戝簡Sun, 24 Apr 2022 06:01:00 GMThttp://www.shnenglu.com/jinq0123/archive/2022/04/24/229292.htmlhttp://www.shnenglu.com/jinq0123/comments/229292.htmlhttp://www.shnenglu.com/jinq0123/archive/2022/04/24/229292.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/229292.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/229292.htmlTortoiseGit is OK but GitExtensions fails

GitExtensions and Git fail:

      "git" pull --progress "origin"
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
Done
Press Enter or Esc to exit...

    

But TortoiseGit is OK.

      git.exe pull --progress -v --no-rebase "origin"
From github.com:jinq0123/recastnavigation
= [up to date]      master      -> origin/master
= [up to date]      release-2.0 -> origin/release-2.0
Already up to date.

    

ssh -T shows that the reason is there is no id files:

      PS d:\github> ssh -vT git@github.com
OpenSSH_for_Windows
debug1: Connecting to github.com [20.205.243.166] port 22.
debug1: Connection established.
debug1: identity file C:\\Users\\jinqing/.ssh/id_rsa type -1
debug1: identity file C:\\Users\\jinqing/.ssh/id_xmss-cert type -1
debug1: Authenticating to github.com:22 as 'git'
debug1: Host 'github.com' is known and matches the ED25519 host key.
debug1: Found key in C:\\Users\\jinqing/.ssh/known_hosts:2
debug1: Will attempt key: C:\\Users\\jinqing/.ssh/id_rsa
debug1: Will attempt key: C:\\Users\\jinqing/.ssh/id_xmss
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Trying private key: C:\\Users\\jinqing/.ssh/id_rsa
debug1: Trying private key: C:\\Users\\jinqing/.ssh/id_xmss
debug1: No more authentication methods to try.
git@github.com: Permission denied (publickey).
PS d:\github>

    

Copy my RSA key file rsa.ppk that TortoiseGit uses as ~/.ssh/id_rsa:

      PS D:\jinqing\github_jinq\recastnavigation> ssh -T git@github.com
Load key "C:\\Users\\jinqing/.ssh/id_rsa": invalid format
git@github.com: Permission denied (publickey).
PS D:\jinqing\github_jinq\recastnavigation>

    

Open TortoiseGit/bin/puttygen.exe, load rsa.ppk, then Convensionis->Export OpenSSH key as ~/.ssh/id_rsa:

      PS D:\jinqing\github_jinq\recastnavigation> ssh -T git@github.com
Hi jinq0123! You've successfully authenticated, ...

    

id_rsa begins with:

      -----BEGIN RSA PRIVATE KEY-----

    


閲戝簡 2022-04-24 14:01 鍙戣〃璇勮
]]>
Tracing usagehttp://www.shnenglu.com/jinq0123/archive/2022/01/02/218556.html閲戝簡閲戝簡Sun, 02 Jan 2022 04:38:00 GMThttp://www.shnenglu.com/jinq0123/archive/2022/01/02/218556.htmlhttp://www.shnenglu.com/jinq0123/comments/218556.htmlhttp://www.shnenglu.com/jinq0123/archive/2022/01/02/218556.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/218556.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/218556.htmlTracing usage

(Jin Qing's Column, Jan., 2022)

Tracing is Rust log crate: https://github.com/tokio-rs/tracing

This example code outputs log to stdout and a log file, using a log filter config file, which can be automatically reloaded on change.

https://gitee.com/jinq0123/tracing-example

Dependencies

Add these dependencies to Cargo.toml:

      [dependencies]
anyhow = "1.0.52"
hotwatch = "0.4.6"
tracing = "0.1.29"
tracing-appender = "0.2.0"
tracing-subscriber = { version = "0.3.5", features = [ "env-filter", "json" ] }

    

main.rs

      mod log;
use anyhow::Result;
use std::{thread, time::Duration};
use tracing::info;
fn main() -> Result<()> {
    let _guard = log::init("./log", "example.log", "config/log_filter.txt")?;
    for i in 0..999 {
        info!(i, "Hello, world!");
        thread::sleep(Duration::from_secs(1));
    }
    Ok(())
}

    

log.rs

      //! Init log.
//!
use anyhow::{anyhow, Context as _, Result};
use hotwatch::{Event, Hotwatch};
use std::fs;
use std::path::Path;
use tracing::{debug, warn, Subscriber};
use tracing_appender::{non_blocking::WorkerGuard, rolling};
use tracing_subscriber::{fmt, layer::SubscriberExt, reload::Handle, EnvFilter};
/// Inits log.
/// Returns a WorkerGuard to ensure buffered logs are flushed,
///  and a Hotwatch to watch the log filter file.
pub fn init(
    directory: impl AsRef<Path>,
    file_name_prefix: impl AsRef<Path>,
    log_filter_file: impl AsRef<Path>,
) -> Result<(WorkerGuard, Hotwatch)> {
    let file_appender = rolling::daily(directory, file_name_prefix);
    let (non_blocking, worker_guard) = tracing_appender::non_blocking(file_appender);
    let file_layer = fmt::Layer::default()
        .with_writer(non_blocking)
        .json()
        .flatten_event(true)
        .with_ansi(false);
    let builder = tracing_subscriber::fmt()
        .pretty()
        .with_env_filter(EnvFilter::from_default_env())
        .with_filter_reloading();
    let handle = builder.reload_handle();
    let subscriber = builder.finish();
    let subscriber = subscriber.with(file_layer);
    tracing::subscriber::set_global_default(subscriber).context("set global default subscriber")?;
    reload_filter(handle.clone(), log_filter_file.as_ref());
    let log_filter_path_buf = log_filter_file.as_ref().to_path_buf();
    let mut hotwatch = Hotwatch::new().context("hotwatch failed to initialize!")?;
    hotwatch
        .watch(log_filter_file.as_ref(), move |event: Event| {
            debug!("log filter file event: {:?}", event);
            if let Event::Write(_) = event {
                reload_filter(handle.clone(), log_filter_path_buf.clone());
            }
        })
        .with_context(|| format!("failed to watch file: {:?}", log_filter_file.as_ref()))?;
    Ok((worker_guard, hotwatch))
}
fn reload_filter<S: Subscriber + 'static>(
    handle: Handle<EnvFilter, S>,
    log_filter_file: impl AsRef<Path>,
) {
    let res = try_reload_filter(handle, log_filter_file);
    match res {
        Ok(_) => debug!("reload log filter OK"),
        Err(e) => warn!("reload log filter error: {:?}", e),
    }
}
fn try_reload_filter<S: Subscriber + 'static>(
    handle: Handle<EnvFilter, S>,
    log_filter_file: impl AsRef<Path>,
) -> Result<()> {
    let contents = fs::read_to_string(log_filter_file.as_ref()).with_context(|| {
        format!(
            "something went wrong reading the file: {:?}",
            log_filter_file.as_ref()
        )
    })?;
    let contents = contents.trim();
    debug!("reload log filter: {:?}", contents);
    let new_filter = contents
        .parse::<EnvFilter>()
        .map_err(|e| anyhow!(e))
        .context("failed to parse env filter")?;
    handle.reload(new_filter).context("handle reload error")
}

    

log_filter.txt

log_filter.txt is the configure file for log. The change of this file will trigger the reload.

The log filter file must exist, otherwise it will be error:

      Error: failed to watch file: "config/log_filter.txt"
Caused by:
    緋葷粺鎵句笉鍒版寚瀹氱殑璺緞銆?(os error 3)

    

The content of log_filter.txt is a single line of filter string. A filter string consists of one or more comma-separated directives. The directive syntax is similar to RUST_LOG env val of env_logger’s.

      target[span{field=value}]=level

    

See: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/struct.EnvFilter.html

Example

  • tracing_example enables logs that:
    • target is modules of tracing_example*
  • info will enable logs that:
    • level is info
  • tracing_ex=info enables logs that:
    • target is modules of tracing_ex*
    • level is info or above
  • info,tracing_ex=debug enables logs that:
    • level is info or above
    • or target is tracing_ex* and level is debug
  • [foo]=trace enables logs that:
    • within the span foo
    • level is trace or above
  • [span_b{name=\"bob\"}] enables logs that:
    • have any target,
    • are inside a span named span_b,
    • which has a field named name with value bob,
    • at any level.

Note: span filter directive will keep effective until it exits the span.



閲戝簡 2022-01-02 12:38 鍙戣〃璇勮
]]>
Try tracinghttp://www.shnenglu.com/jinq0123/archive/2022/01/01/218312.html閲戝簡閲戝簡Sat, 01 Jan 2022 12:35:00 GMThttp://www.shnenglu.com/jinq0123/archive/2022/01/01/218312.htmlhttp://www.shnenglu.com/jinq0123/comments/218312.htmlhttp://www.shnenglu.com/jinq0123/archive/2022/01/01/218312.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/218312.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/218312.htmlTry tracing

(Jin Qing's Column, Dec., 2021)

https://github.com/tokio-rs/tracing

Usage on github

Switch to tag tracing-0.1.29 first. The master's examples can not build. Subscriber is renaming to collector on master.

tracingsubscriber in 0.1.29 has been renamed to tracingcollect on master.

  • In Applications
  • In Libraries
  • In async

The example is using tracing_subscriber::fmt, which can be configured by RUST_LOG env val.

By default, no log is output. Set RUST_LOG=info before running, and the log is:

      Dec 25 10:23:11.200  INFO tracing_test: preparing to shave yaks number_of_yaks=3
Dec 25 10:23:11.203  INFO tracing_test: yak shaving completed. all_yaks_shaved=true

    
  • span
  • set_global_default()
  • tracingcollectwith_default()

Usage of RUST_LOG.

RUST_LOG is defined in env_logger

  • Default disabled except error
  • RUST_LOG is comma-separated directives
    • Directive form: examplelogtarget=level
      • The log target is a prefix
        • If target is ommitted, then all modules are set
      • The level is "all" if ommitted
        • off, error, warn, info, debug, trace
    • Regex filter: error,hello=warn/[0-9]scopes
      • For all directives

Usage on crates.io

It is slightly different from the github.

  • Uses set_global_default()
  • Explains some concepts
    • span
    • event

Span filter? [my_span]=info

Document

https://docs.rs/tracing/0.1.29/tracing/

The document is much more detailed than github and crates.io.

Code examples

Run example:

      cargo run --example counters

    

Write to file

fmt-multiple-writers:

  • Change to write to current dir
    • tracing_appender::rolling::hourly(".", ...
  • Do not write ANSI color to file
    • .with(fmt::Layer::new().with_writer(non_blocking).with_ansi(false));

Dynamic level

tower-load

  • with_filter_reloading() -> reload handl -> handle.reload(new_filter)
  • new filter is send through http

https://github.com/tokio-rs/tracing/issues/1629 tracing_subscriber::reload

They use the same tracing_subscriber::reload::Handle::reload()

Filters and Layers

https://docs.rs/tracing-subscriber/0.3.4/tracing_subscriber/layer/index.html#filtering-with-layers

Reload from config file

It is desired to reconfigure the log filter in the runtime by SIGHUP, or watch configure file change. It can use notify crate to watch file.

      use anyhow::{anyhow, Context as _, Result};
use hotwatch::{Event, Hotwatch};
use std::{fs, thread, time::Duration};
use tracing::{debug, info, warn, Subscriber};
use tracing_subscriber::{reload::Handle, EnvFilter};
const CFG: &str = "cfg.txt";
fn main() -> Result<()> {
    let builder = tracing_subscriber::fmt()
        .with_env_filter(EnvFilter::from_default_env())
        .with_filter_reloading();
    let handle = builder.reload_handle();
    builder
        .try_init()
        .map_err(|e| anyhow!(e))
        .context("failed to init subscriber builder")?;
    tracing::info!("Before reload");
    reload_cfg(handle.clone());
    let mut hotwatch = Hotwatch::new().context("hotwatch failed to initialize!")?;
    hotwatch
        .watch(CFG, move |event: Event| {
            if let Event::Write(_) = event {
                reload_cfg(handle.clone());
            }
        })
        .context("failed to watch file")?;
    for i in 0..999 {
        info!(i);
        thread::sleep(Duration::from_secs(1));
    }
    Ok(())
}
fn reload_cfg<S: Subscriber + 'static>(handle: Handle<EnvFilter, S>) {
    let res = try_reload_cfg(handle);
    match res {
        Ok(_) => debug!("reload cfg OK"),
        Err(e) => warn!("reload cfg error: {:?}", e),
    }
}
fn try_reload_cfg<S: Subscriber + 'static>(handle: Handle<EnvFilter, S>) -> Result<()> {
    let contents = fs::read_to_string(CFG).context("something went wrong reading the file")?;
    let contents = contents.trim();
    debug!("reload cfg: {:?}", contents);
    let new_filter = contents
        .parse::<EnvFilter>()
        .map_err(|e| anyhow!(e))
        .context("failed to parse env filter")?;
    handle.reload(new_filter).context("handle reload error")
}

    

How to new a Subscriber with tracing-subscriber

  • SubscriberBuilder:
  • Registry:
    • registry().with...()
    • Currently, the [Registry] type provided by this crate is the only [Subscriber] implementation capable of participating in per-layer filtering.
  • FmtSubscriber
    • new it manually
    • need SubscriberBuilder to configure


閲戝簡 2022-01-01 20:35 鍙戣〃璇勮
]]>
Box<dyn Trait> doesn't implement the traithttp://www.shnenglu.com/jinq0123/archive/2021/12/21/217886.html閲戝簡閲戝簡Tue, 21 Dec 2021 07:09:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/12/21/217886.htmlhttp://www.shnenglu.com/jinq0123/comments/217886.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/12/21/217886.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217886.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217886.html From: 

By default a Box<dyn Trait> doesn't implement the trait of the object it contains. This means that trying to construct PeopleZoo<Box<dyn Person>> won't work out of the box and will give a type error.

Because of this, it's good practice to give a default implementation of your trait for it's boxed counterpart. This can be done by calling as_ref or as_mut on the Box and calling the references relevant method.

For just a small bit of effort you can help a bunch of people that may consume your struct.

trait Person { fn say_hello(&self); } impl Person for Box<dyn Person> { fn say_hello(&self) { self.as_ref().say_hello() } }

 

struct PeopleZoo<P: Person> {
    people: Vec<P>,
}
fn main() { let mut zoo: PeopleZoo<Box<dyn Person>> = PeopleZoo { people: vec![] }; }


閲戝簡 2021-12-21 15:09 鍙戣〃璇勮
]]>
minikube DNS fails after SRV queryhttp://www.shnenglu.com/jinq0123/archive/2021/12/10/217878.html閲戝簡閲戝簡Fri, 10 Dec 2021 02:46:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/12/10/217878.htmlhttp://www.shnenglu.com/jinq0123/comments/217878.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/12/10/217878.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217878.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217878.htmlminikube DNS fails after SRV query

(Jin Qing's Column, Dec., 2021)

My program is using K8s DNS SRV query to discovery service, and when it's deployed on minikube, I find DNS failure.

I can use nslookup to reproduce the failure.

Querying a FQDN is OK. But after querying a non-existing SRV short name, the ping fails.

root@web-0:/# ping google.com
PING google.com (142.250.66.110) 56(84) bytes of data.
64 bytes from hkg12s28-in-f14.1e100.net (142.250.66.110): icmp_seq=1 ttl=108 time=33.7 ms
64 bytes from hkg12s28-in-f14.1e100.net (142.250.66.110): icmp_seq=2 ttl=108 time=33.8 ms
^C
--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 33.779/33.834/33.889/0.055 ms
root@web-0:/# nslookup
> set type=srv
> nosuch-nosuch-nosuch-1234567890abcdefg.cn
Server:         10.96.0.10
Address:        10.96.0.10#53
** server can't find nosuch-nosuch-nosuch-1234567890abcdefg.cn: NXDOMAIN
> exit
root@web-0:/# ping google.com
PING google.com (142.250.66.110) 56(84) bytes of data.
64 bytes from hkg12s28-in-f14.1e100.net (142.250.66.110): icmp_seq=1 ttl=108 time=33.7 ms
64 bytes from hkg12s28-in-f14.1e100.net (142.250.66.110): icmp_seq=2 ttl=108 time=33.7 ms
^C
--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 33.730/33.735/33.741/0.183 ms
root@web-0:/# nslookup
> set type=srv
> nginx-wrong
Server:         10.96.0.10
Address:        10.96.0.10#53
** server can't find nginx-wrong: SERVFAIL
> exit
root@web-0:/# ping google.com
ping: unknown host google.com
root@web-0:/# 

The ping will recover to normal after about 1 minute.

If I query a existing internal service name, and nslookup returns correctly, then DNS is OK after I quit nslookup.

root@web-0:/# ping google.com
PING google.com (142.250.66.110) 56(84) bytes of data.
64 bytes from hkg12s28-in-f14.1e100.net (142.250.66.110): icmp_seq=1 ttl=108 time=33.6 ms
64 bytes from hkg12s28-in-f14.1e100.net (142.250.66.110): icmp_seq=2 ttl=108 time=34.8 ms
^C
--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 33.648/34.260/34.872/0.612 ms
root@web-0:/# nslookup
> set type=srv
> nginx
Server:         10.96.0.10
Address:        10.96.0.10#53
nginx.default.svc.cluster.local service = 0 25 80 web-1.nginx.default.svc.cluster.local.
nginx.default.svc.cluster.local service = 0 25 80 web-2.nginx.default.svc.cluster.local.
nginx.default.svc.cluster.local service = 0 25 80 web-0.nginx.default.svc.cluster.local.
nginx.default.svc.cluster.local service = 0 25 80 web-3.nginx.default.svc.cluster.local.
> exit
root@web-0:/# ping google.com
PING google.com (142.250.66.110) 56(84) bytes of data.
64 bytes from hkg12s28-in-f14.1e100.net (142.250.66.110): icmp_seq=1 ttl=108 time=33.5 ms
^C
--- google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 33.529/33.529/33.529/0.000 ms
root@web-0:/# 

When DNS fails, the whole cluster can not query any domain name outside, but internal name is OK.

https://github.com/kubernetes/minikube/issues/13137



閲戝簡 2021-12-10 10:46 鍙戣〃璇勮
]]>
DeathVoteExpirationTimeout in Orleanshttp://www.shnenglu.com/jinq0123/archive/2021/12/08/217872.html閲戝簡閲戝簡Wed, 08 Dec 2021 01:43:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/12/08/217872.htmlhttp://www.shnenglu.com/jinq0123/comments/217872.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/12/08/217872.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217872.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217872.htmlDeathVoteExpirationTimeout in Orleans

(Jin Qing's Column, Dec., 2021)

Try to find out why Orleans need a DeathVoteExpirationTimeout config.

https://dotnet.github.io/orleans/docs/implementation/cluster_management.html#extension-to-totally-order-membership-views

DeathVoteExpirationTimeout - Expiration time in seconds for death vote in the membership table. Default is 120 seconds
GetFreshVotes

DeathVoteExpirationTimeout is only used by GetFreshVotes(), which has 3 occurence:

    class ClusterHealthMonitor
    {
        ...
        private UpdateMonitoredSilos(...)
        {
            ...
            bool isSuspected = candidateEntry.GetFreshVotes(now, DeathVoteExpirationTimeout).Count > 0;
            ...
        }
    }
    class LocalSiloHealthMonitor
    {
        ...
        private int CheckSuspectingNodes(DateTime now, List<string> complaints)
        {
            ...
            var freshVotes = membershipEntry.GetFreshVotes(now, DeathVoteExpirationTimeout);
            ...
        }
        ...
    }
    class MembershipTableManager
    {
        ...
        public TryToSuspectOrKill(SiloAddress silo)
        {
            ...
            // get all valid (non-expired) votes
            var freshVotes = entry.GetFreshVotes(DateTime.UtcNow, DeathVoteExpirationTimeout);
            ...
        }
        ...
    }

GetFreshVotes() uses this expiration time to ignore old voter:

        internal GetFreshVotes(DateTime now, TimeSpan expiration)
        {
            ...
            foreach (var voter in this.SuspectTimes)
            {
                var otherVoterTime = voter.Item2;
                if (now.Subtract(otherVoterTime) < expiration)
                {
                    result.Add(voter);
                }
            }
            return result.ToImmutable();
        }


閲戝簡 2021-12-08 09:43 鍙戣〃璇勮
]]>
How to delete local branches of GitExtensionhttp://www.shnenglu.com/jinq0123/archive/2021/12/07/217870.html閲戝簡閲戝簡Tue, 07 Dec 2021 09:04:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/12/07/217870.htmlhttp://www.shnenglu.com/jinq0123/comments/217870.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/12/07/217870.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217870.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217870.htmlHow to delete local branches of GitExtension

(Jin Qing's Column, Dec., 2021)

GitExtension is a good tool. After a long time of usage, my branch list finally reaches over a full screen, and it is hard to select the branch I want.

GitExtension always remembers all the branches of remote and local, even they have been deleted.

I tried to find a way to delete these branches which are already merged, but resulted in futile.

It has a plugin names "Delete obsolete branches", but I don't know how to use it.

Finally I renamed the work directory, and cloned a new one, which clears all the branches. It seems that these branches are stored in local .git directory. If let GitExt open the renamed directory, these branches reappears.

Reference:



閲戝簡 2021-12-07 17:04 鍙戣〃璇勮
]]>
寤鴻鏃ュ織 slog 鏀規(guī)崲 tracinghttp://www.shnenglu.com/jinq0123/archive/2021/11/24/217865.html閲戝簡閲戝簡Wed, 24 Nov 2021 10:18:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/11/24/217865.htmlhttp://www.shnenglu.com/jinq0123/comments/217865.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/11/24/217865.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217865.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217865.html寤鴻鏃ュ織 slog 鏀規(guī)崲 tracing

(閲戝簡鐨勪笓鏍?2021.11)

  • slog 涓嶆槸绔嬪嵆鍐欑洏錛屾渶鍚庣殑鍑犺鏃ュ織浼氫涪澶?/p>

    • tracing 涓嶄細涓㈡棩蹇?/li>
  • slog 涓嶆敮鎸佽繍琛屼腑鍔ㄦ佹洿鏀規(guī)棩蹇楃瓑綰?/p>

  • slog 涓嶆敮鎸佷笉鍚屽寘璁劇疆涓嶅悓鏃ュ織絳夌駭

    • tracing 鍙崟鐙墦寮鏌愪釜妯″潡鐨勬棩蹇?/li>
  • 鍙戠幇涓嬈?slog panic: panicked at 'slog::Fuse Drain: Fatal(Custom { kind: BrokenPipe, error: "The logger thread terminated" })'

    • 鎼滃埌鐩稿悓閿欒錛?a target='_blank' class='url'>https://github.com/mimblewimble/grin/issues/946

      • 鍒漢鐨勮В鍐蟲柟娉曟槸: 涓嶇敤slog浜?/li>
  • slog 涓婚〉10鏈堟坊鍔犳帹鑽愪嬌鐢?tracing

  • slog 鎸夌嚎紼嬩繚瀛樹笂涓嬫枃錛宼racing 鎸夊崗紼嬩繚瀛樹笂涓嬫枃

  • tracing github star 1.9K > slog 1.2K

  • tracing crates 涓嬭澆 21M > sloggers 0.4M, slog-scope 2.6M, slog-stdlog 1.9M

  • tracing vs slog: https://www.reddit.com/r/rust/comments/kdo29n/slog_vs_tracing_which_one_do_you_prefer/

  • Rust compiler 2020騫村凡鏀圭敤 tracing錛?a target='_blank' class='url'>https://github.com/rust-lang/rust/pull/74726

  • tracing 鏇村ソ鐢?/p>

    • 鏂囨。鍙婄ず渚嬫洿鍏?/li>
    • 鏀寔搴撴洿澶?/li>


閲戝簡 2021-11-24 18:18 鍙戣〃璇勮
]]>
Named parameters in Rusthttp://www.shnenglu.com/jinq0123/archive/2021/11/18/217862.html閲戝簡閲戝簡Thu, 18 Nov 2021 05:14:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/11/18/217862.htmlhttp://www.shnenglu.com/jinq0123/comments/217862.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/11/18/217862.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217862.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217862.htmlNamed parameters in Rust

(Jin Qing's Column, Nov. 18, 2021)

There is no "named function parameters" in Rust, but there is an idiom for this case. If a function needs many parameters, we can define a type with all these parameters as fields, and implement Default trait for this type. Then we can input this type as the function parameter, with some of the fields specified and others using default.

foo(Config {
    a: 123,
    b: bool,
    ...Default::default(),
});


閲戝簡 2021-11-18 13:14 鍙戣〃璇勮
]]>
Rust traits act as generic constraintshttp://www.shnenglu.com/jinq0123/archive/2021/11/18/217861.html閲戝簡閲戝簡Thu, 18 Nov 2021 04:51:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/11/18/217861.htmlhttp://www.shnenglu.com/jinq0123/comments/217861.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/11/18/217861.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217861.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217861.htmlRust traits act as generic constraints

(Jin Qing's Column, Nov. 18, 2021)

Rust traits are different from interfaces of C++/Java/Go.

See: https://stevedonovan.github.io/rustifications/2018/09/08/common-rust-traits.html

Rust traits are mechanism for adding behavior to types.

Traits have 2 modes. One is interface as Java.

Another is generic constraint. Generic functions are defined over types that implemented specific traits.

The "complie-time duck typing" in C++ templates is avoided in Rust. Rust will reject a type with quack() method as a Duck type. We must pass a type which implements Duck trait. But in Go, a type with quack() method is sufficient to be used as a Duck interface.

 



閲戝簡 2021-11-18 12:51 鍙戣〃璇勮
]]>
GLIBC_2.29 not foundhttp://www.shnenglu.com/jinq0123/archive/2021/11/05/217853.html閲戝簡閲戝簡Fri, 05 Nov 2021 01:42:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/11/05/217853.htmlhttp://www.shnenglu.com/jinq0123/comments/217853.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/11/05/217853.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217853.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217853.htmlGLIBC_2.29 not found

(Jin Qing's Column, Nov. 4, 2021)

My Rust program failed when running in docker:

root@a26b49c91efb:/myapp# ldd libmyapp_py.so
./libmyapp_py.so: /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found (required by ./libmyapp_py.so)

The problem is because I build in "rust:1.56", and run in "debian:buster-slim" which is quite old. Run dpkg -l libc-bin shows the libc version is 2.28:

C:\Users\jinqing01>docker run debian:buster-slim dpkg -l libc-bin
Unable to find image 'debian:buster-slim' locally
buster-slim: Pulling from library/debian
b380bbd43752: Already exists
Digest: sha256:544c93597c784cf68dbe492ef35c00de7f4f6a990955c7144a40b20d86a3475f
Status: Downloaded newer image for debian:buster-slim
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name           Version      Architecture Description
+++-==============-============-============-=================================
ii  libc-bin       2.28-10      amd64        GNU C Library: Binaries

Using the latest "debian:bookworm-slim" solved the problem.

Dockerfile:

FROM rust:1.56 as chef
RUN cargo install cargo-chef
WORKDIR /myapp
FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
FROM chef AS builder
COPY --from=planner /myapp/recipe.json recipe.json
# Build dependencies
RUN cargo chef cook --release --recipe-path recipe.json
# Build application
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim AS runtime
WORKDIR /myapp
RUN apt-get update && apt-get install -y python3 python3-pip && rm -rf /var/lib/apt/lists/*
RUN pip3 install protobuf
COPY --from=builder /myapp/target/release/*.so /myapp/
COPY --from=builder /myapp/target/release/myapp /myapp/
COPY --from=builder /myapp/tests /myapp/tests
CMD ["myapp"]


閲戝簡 2021-11-05 09:42 鍙戣〃璇勮
]]>
Clustering provider in Orleanshttp://www.shnenglu.com/jinq0123/archive/2021/11/03/217852.html閲戝簡閲戝簡Wed, 03 Nov 2021 05:36:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/11/03/217852.htmlhttp://www.shnenglu.com/jinq0123/comments/217852.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/11/03/217852.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217852.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217852.htmlClustering provider in Orleans

(Jin Qing's Column, Nov. 3, 2021)

When deployed to a cluster of nodes, Orleans internally implements a protocol to manage it's silos, including discovery, failure and reconfigure, which is called cluster membership management.

Orleans has clustering membership providers for: Azure, SQL server, Zookeeper.

Clustering provider is one of key aspects of silo configuration.

Orleans.Clustering.Kubernetes

OrleansContrib/Orleans.Clustering.Kubernetes is a clustering provider for running Orleans cluster on Kubernetes.

Tell silo to use Kubernetes as the Cluster Membership Provider:

var silo = new SiloBuilder()
        ...
        .UseKubeMembership()
        ...
        .Build();

Interface

UseKubeMembership() instantiates a KubeMembershipTable which implements IMembershipTable.

    public interface IMembershipTable
    {
        Task InitializeMembershipTable(bool tryInitTableVersion);
        Task DeleteMembershipTableEntries(string clusterId);
        Task CleanupDefunctSiloEntries(DateTimeOffset beforeDate);
        Task<MembershipTableData> ReadRow(SiloAddress key);
        Task<MembershipTableData> ReadAll();
        Task<bool> InsertRow(MembershipEntry entry, TableVersion tableVersion);
        Task<bool> UpdateRow(MembershipEntry entry, string etag, TableVersion tableVersion);
        Task UpdateIAmAlive(MembershipEntry entry);
    }

Implement

KubeMembershipTable access Kubernetes API server to read and write silo entry CRD.

  • InitializeMembershipTable()

    • TryInitClusterVersion()

      • _kubeClient.GetNamespacedCustomObjectAsync
      • _kubeClient.CreateNamespacedCustomObjectAsync
  • DeleteMembershipTableEntries(string clusterId)

    • _kubeClient.DeleteNamespacedCustomObjectAsync
  • InsertRow(...)

    • _kubeClient.GetNamespacedCustomObjectAsync
    • _kubeClient.ReplaceNamespacedCustomObjectAsync
    • _kubeClient.CreateNamespacedCustomObjectAsync
  • ReadAll()

    • GetClusterVersion()

      • _kubeClient.ListNamespacedCustomObjectAsync
    • GetSilos()

      • _kubeClient.ListNamespacedCustomObjectAsync
  • ReadRow(SiloAddress key)

    • _kubeClient.GetNamespacedCustomObjectAsync
  • UpdateIAmAlive(MembershipEntry entry)

    • _kubeClient.GetNamespacedCustomObjectAsync
    • _kubeClient.ReplaceNamespacedCustomObjectAsync
  • UpdateRow(...)

    • _kubeClient.ReplaceNamespacedCustomObjectAsync
  • CleanupDefunctSiloEntries(DateTimeOffset beforeDate)

    • _kubeClient.DeleteNamespacedCustomObjectAsync

The operators to NamespacedCustomObject are:

  • Delete
  • Get
  • Create
  • Replace
  • List

CRD

Two CRDs, ClusterVersion and Silo are defined in files:

  • ClusterVersionCRD.yaml
  • SiloEntryCRD.yaml

Custom resource objects are stored in etcd of Kubernetes.



閲戝簡 2021-11-03 13:36 鍙戣〃璇勮
]]>
Why Orleans' actor is virutalhttp://www.shnenglu.com/jinq0123/archive/2021/11/02/217850.html閲戝簡閲戝簡Tue, 02 Nov 2021 07:27:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/11/02/217850.htmlhttp://www.shnenglu.com/jinq0123/comments/217850.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/11/02/217850.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217850.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217850.htmlWhy Orleans' actor is virutal

(Jin Qing's Column, Nov. 2, 2021)

Virtual Actor is a concept invented by Microsoft Orleans, which is a framework of distributed actor.

Orleans: Distributed Virtual Actors for Programmability and Scalability describes the virtual programming model.

The virtual actor is analogous to virtual memory. Virtual actors are mapped to physical arctors instances in the running servers. Virtualization of actors in Orleans has 4 facets:

  1. Perpetual existence

    • Actors always exist, virtually
    • Actors can not be created or destroied explicitly
    • Server failure does not affect the actors' existence
  2. Automatic instantiation

    • Activation: Orleans automatically create an actor
    • A request triggers an activation if the actor doesn't exist
    • Unused actors are automatically reclaimed
  3. Location transparency

    • Applications don't know where the physical actor is
    • Similar to virtual memory's "paged out" and mapping
  4. Automatic scale out

    • 2 activation modes:

      • Single activation (default): Only one simultaneous actor is allowed

      • Stateless worker: Many activations of an actor are created

        • to increase throughput

Actor viruliaztion greatly simplifes the programming, since it gets rid of the burden of actor lifecycle control.

 

 



閲戝簡 2021-11-02 15:27 鍙戣〃璇勮
]]>
What comes after microservice?http://www.shnenglu.com/jinq0123/archive/2021/10/29/217843.html閲戝簡閲戝簡Fri, 29 Oct 2021 03:16:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/10/29/217843.htmlhttp://www.shnenglu.com/jinq0123/comments/217843.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/10/29/217843.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217843.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217843.htmlWhat comes after microservice?

(Jin Qing's Column, Oct. 25, 2021)

Reading "The Evolution of Distributed Systems on Kubernetes" from Bilgin Ibryam.

https://www.infoq.com/articles/distributed-systems-kubernetes/

What are the purpose of projects like Dapr, Istio, Knative? How can they change the world?

Modern distributed systems

The needs of distributed systems:

  • Business logic

  • Other

    • Lifecycle: deploy, rollback, upgrade, scale, stop, isolate, config
    • Network: connect, circuit break, retry, timeout, load balance, discover, security, trace
    • Resource binding: to API, protocol, message, data format
    • State: stateless, stateful, store

Monolithic architecture

enterprise service bus (ESB): not distributed

Cloud-native architecture

Kubernetes: Lifecycle

Service Mesh

Istio: Networking

Serverless

Knative: scale

Dapr

Networking, resource binding, state

  • Lifecycle: Kubernetes + operator
  • Networking: Envoy
  • Binding: Apache Camel
  • State: Cloudstate

Multi-runtime microservice

Write business logic as another runtime. Here runtime is a process?

What comes after microservice

Faas is not the best. Multi-runtime microservice maybe is.

Mecha architecture

 



閲戝簡 2021-10-29 11:16 鍙戣〃璇勮
]]>
Display Non-UTF-8 outputhttp://www.shnenglu.com/jinq0123/archive/2021/10/27/217841.html閲戝簡閲戝簡Wed, 27 Oct 2021 04:22:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/10/27/217841.htmlhttp://www.shnenglu.com/jinq0123/comments/217841.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/10/27/217841.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217841.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217841.htmlDisplay Non-UTF-8 output
(Jin Qing's Column, Oct. 27, 2021)
Cargo build output Non-UTF-8. Use python to decode it as gbk and print.
```
C:\Users\jinqing01>python
>>> s = b'Non-UTF-8 output:   \xd5\xfd\xd4\xda\xb4\xb4\xbd\xa8\xbf\xe2 E:\\gitlab\\myproj\\target\\debug\\deps\\pyext.dll.lib \xba\xcd\xb6\xd4\xcf\xf3 E:\\gitlab\\myproj\\target\\debug\\deps\\pyext.dll.exp\r\npyext.2rtvxajnfydf6uo2.rcgu.o : error LNK2019: \xce\xde\xb7\xa8\xbd\xe2\xce\xf6\xb5\xc4\xcd\xe2\xb2\xbf\xb7\xfb\xba\xc5 __imp_PyUnicode_AsUTF8\xa3\xac\xba\xaf\xca\xfd _ZN116_$LT$pyext..script..pyext..py_object_proxy..PyNonTxnStates$u20$as$u20$pyext..object..state..NonTxnStates$GT$14to_mysql_value17h5dbfad19064f6106E \xd6\xd0\xd2\xfd\xd3\xc3\xc1\xcb\xb8\xc3\xb7\xfb\xba\xc5\r\npyext.3i2r75wtstbttq2v.rcgu.o : error LNK2001: \xce\xde\xb7\xa8\xbd\xe2\xce\xf6\xb5\xc4\xcd\xe2\xb2\xbf\xb7\xfb\xba\xc5 __imp_PyUnicode_AsUTF8\r\nlibpyutil-98f7dde2ceee0f61.rlib(pyutil-98f7dde2ceee0f61.2r21sryt0zfxlscx.rcgu.o) : error LNK2001: \xce\xde\xb7\xa8\xbd\xe2\xce\xf6\xb5\xc4\xcd\xe2\xb2\xbf\xb7\xfb\xba\xc5 __imp_PyUnicode_AsUTF8\r\nE:\\gitlab\\myproj\\target\\debug\\deps\\pyext.dll : fatal error LNK1120: 1 \xb8\xf6\xce\xde\xb7\xa8\xbd\xe2\xce\xf6\xb5\xc4\xcd\xe2\xb2\xbf\xc3\xfc\xc1\xee\r\n'
>>> s.decode('gbk')
'Non-UTF-8 output:   姝e湪鍒涘緩搴?E:\\gitlab\\myproj\\target\\debug\\deps\\pyext.dll.lib 鍜屽璞?E:\\gitlab\\myproj\\target\\debug\\deps\\pyext.dll.exp\r\npyext.2rtvxajnfydf6uo2.rcgu.o : error LNK2019: 鏃犳硶瑙f瀽鐨勫閮?絎﹀彿 __imp_PyUnicode_AsUTF8錛屽嚱鏁?_ZN116_$LT$pyext..script..pyext..py_object_proxy..PyNonTxnStates$u20$as$u20$pyext..object..state..NonTxnStates$GT$14to_mysql_value17h5dbfad19064f6106E 涓紩鐢ㄤ簡璇ョ鍙穃r\npyext.3i2r75wtstbttq2v.rcgu.o : error LNK2001: 鏃犳硶瑙f瀽鐨勫閮ㄧ鍙?__imp_PyUnicode_AsUTF8\r\nlibpyutil-98f7dde2ceee0f61.rlib(pyutil-98f7dde2ceee0f61.2r21sryt0zfxlscx.rcgu.o) : error LNK2001: 鏃犳硶瑙f瀽鐨勫閮ㄧ鍙?__imp_PyUnicode_AsUTF8\r\nE:\\gitlab\\myproj\\target\\debug\\deps\\pyext.dll : fatal error LNK1120: 1 涓棤娉曡В鏋愮殑澶栭儴鍛戒護\r\n'
>>>
```


閲戝簡 2021-10-27 12:22 鍙戣〃璇勮
]]>
Advice for Rust library writers about Errorhttp://www.shnenglu.com/jinq0123/archive/2021/09/02/217805.html閲戝簡閲戝簡Thu, 02 Sep 2021 03:24:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/09/02/217805.htmlhttp://www.shnenglu.com/jinq0123/comments/217805.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/09/02/217805.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217805.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217805.htmlAdvice for library writers

Idioms for Rust libraries are still forming, but if your library needs to report custom errors, then you should probably define your own error type. It’s up to you whether or not to expose its representation (like ErrorKind) or keep it hidden (like ParseIntError). Regardless of how you do it, it’s usually good practice to at least provide some information about the error beyond just its String representation. But certainly, this will vary depending on use cases.

At a minimum, you should probably implement the Error trait. This will give users of your library some minimum flexibility for composing errors. Implementing the Error trait also means that users are guaranteed the ability to obtain a string representation of an error (because it requires impls for both fmt::Debug and fmt::Display).

Beyond that, it can also be useful to provide implementations of From on your error types. This allows you (the library author) and your users to compose more detailed errors. For example, csv::Error provides From impls for both io::Error and byteorder::Error.

Finally, depending on your tastes, you may also want to define a Result type alias, particularly if your library defines a single error type. This is used in the standard library for io::Result and fmt::Result.

From: Error Handling in Rust - Andrew Gallant's Blog (burntsushi.net)



閲戝簡 2021-09-02 11:24 鍙戣〃璇勮
]]>
How to show the first few errors of rustchttp://www.shnenglu.com/jinq0123/archive/2021/09/01/217804.html閲戝簡閲戝簡Wed, 01 Sep 2021 02:55:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/09/01/217804.htmlhttp://www.shnenglu.com/jinq0123/comments/217804.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/09/01/217804.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217804.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217804.html
# How to show the first few errors of rustc

(Jin Qing's Column, Sep. 1, 2021)

When using VS Code to develop Rust, "cargo check" output maybe thousands of lines.
Use "cargo check --color=always 2>&1 | head -n100" to limit the output.

1. Change powershell terminal to cmd, because powershell fails to use pipe
    * Terminal -> New Terminal to open a terminal
    * Open the dropdown list by click the down arrow of "+" on the top-left of terminal
    * Select "Command Prompt"
2. Type and run command: `cargo test 2>&1 --color always | "d:\Program Files\Git\usr\bin\head.exe" -n100`
    * You should change the path of head.exe


閲戝簡 2021-09-01 10:55 鍙戣〃璇勮
]]>
&'a mut self is restrictivehttp://www.shnenglu.com/jinq0123/archive/2021/08/25/217794.html閲戝簡閲戝簡Wed, 25 Aug 2021 05:32:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/08/25/217794.htmlhttp://www.shnenglu.com/jinq0123/comments/217794.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/08/25/217794.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217794.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217794.html
# &'a mut self is restrictive

From https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md#5-if-it-compiles-then-my-lifetime-annotations-are-correct

```
#[derive(Debug)]
struct NumRef<'a>(&'a i32);

impl<'a> NumRef<'a> {
    // my struct is generic over 'a so that means I need to annotate
    // my self parameters with 'a too, right? (answer: no, not right)
    fn some_method(&'a mut self) {}
}

fn main() {
    let mut num_ref = NumRef(&5);
    num_ref.some_method(); // mutably borrows num_ref for the rest of its lifetime
    num_ref.some_method(); // 鉂?/div>
    println!("{:?}", num_ref); // 鉂?/div>
}
```

If we have some struct generic over 'a we almost never want to write a method with a &'a mut self receiver. 
What we're communicating to Rust is "this method will mutably borrow the struct for the entirety of the struct's lifetime". 
In practice this means Rust's borrow checker will only allow at most one call to some_method 
before the struct becomes permanently mutably borrowed and thus unusable. 
The use-cases for this are extremely rare but the code above is very easy for confused beginners to write and it compiles. 
The fix is to not add unnecessary explicit lifetime annotations and let Rust's lifetime elision rules handle it:

```
#[derive(Debug)]
struct NumRef<'a>(&'a i32);

impl<'a> NumRef<'a> {
    // no more 'a on mut self
    fn some_method(&mut self) {}

    // above line desugars to
    fn some_method_desugared<'b>(&'b mut self){}
}

fn main() {
    let mut num_ref = NumRef(&5);
    num_ref.some_method();
    num_ref.some_method(); // 鉁?/div>
    println!("{:?}", num_ref); // 鉁?/div>
}
```


閲戝簡 2021-08-25 13:32 鍙戣〃璇勮
]]>Rust callback idiomhttp://www.shnenglu.com/jinq0123/archive/2021/08/24/217793.html閲戝簡閲戝簡Tue, 24 Aug 2021 02:19:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/08/24/217793.htmlhttp://www.shnenglu.com/jinq0123/comments/217793.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/08/24/217793.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217793.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217793.html
# Rust callback idiom

Code is from: https://stackoverflow.com/questions/41081240/idiomatic-callbacks-in-rust
and
https://morestina.net/blog/793/closure-lifetimes-in-rust

```
struct Processor<'a> {
    callback: Box<dyn FnMut() + 'a>,
}

impl<'a> Processor<'a> {
    fn new() -> Processor<'a> {
        Processor {
            callback: Box::new(|| ()),
        }
    }

    fn set_callback(&mut self, c: impl FnMut() + 'a) {
        self.callback = Box::new(c);
    }

    fn process_events(&mut self) {
        (self.callback)();
    }
}

fn simple_callback() {
    println!("hello");
}

fn main() {
    let _ = Processor::new();
    
    let mut p = Processor {
        callback: Box::new(simple_callback),
    };
    p.process_events();
    let s = "world!".to_string();
    let callback2 = move || println!("hello {}", s);
    p.set_callback(callback2);
    p.process_events();
}
```

Note:
* "impl FnMut()" can only used in function declaration, not in struct declaration.
* dyn FnMut() is unsized, so it must be stored in Box
* set_callback(&mut self, c: impl FnMut()) need a lifetime for c to tell compiler that c outlives structure
    + rustc suggests `impl FnMut() + 'static`, but that is too restrictive
        - In most cases, we do not have a static lifetimed callback
* FnMut() is more restrictive than FnOnce(), but FnOnce() can only be called once
* set_callback(...) is a template method, because each closure has a different type


閲戝簡 2021-08-24 10:19 鍙戣〃璇勮
]]>Rust variable rebinding for closurehttp://www.shnenglu.com/jinq0123/archive/2021/08/22/217787.html閲戝簡閲戝簡Sun, 22 Aug 2021 09:25:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/08/22/217787.htmlhttp://www.shnenglu.com/jinq0123/comments/217787.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/08/22/217787.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217787.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217787.html
From: https://rust-unofficial.github.io/patterns/idioms/pass-var-to-closure.html

Rust closure captures variables by reference by default.
You can use "move" keyword to change to move variables.
In most cases, some variables are moved, and some are referenced.
We can rebind variables in a separate scope before move to the closure.

```
use std::rc::Rc;

let num1 = Rc::new(1);
let num2 = Rc::new(2);
let num3 = Rc::new(3);
let closure = {
    // `num1` is moved
    let num2 = num2.clone();  // `num2` is cloned
    let num3 = num3.as_ref();  // `num3` is borrowed
    move || {
        *num1 + *num2 + *num3;
    }
};
```

Or we can use additional variables to move in the same scope:
```
let num2_clone = num2.clone();
let num3_ref = num3.as_ref();
let closure = move || {
    *num1 + *num2_clone + *num3_ref
}
```

Using separate scope can reuse the variable name, which make the closure body clearer,
but need additional indent.

閲戝簡 2021-08-22 17:25 鍙戣〃璇勮
]]>
Rust Deref coercion examplehttp://www.shnenglu.com/jinq0123/archive/2021/08/22/217786.html閲戝簡閲戝簡Sun, 22 Aug 2021 04:31:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/08/22/217786.htmlhttp://www.shnenglu.com/jinq0123/comments/217786.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/08/22/217786.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217786.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217786.html
https://doc.rust-lang.org/std/ops/trait.Deref.html

```
use std::ops::Deref;

struct DerefExample<T> {
    value: T
}

impl<T> Deref for DerefExample<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.value
    }
}

let x = DerefExample { value: 'a' };
assert_eq!('a', *x);
```

Deref coercion can be used in newtype:
```
struct MyI32(i32)

impl Deref for MyI32 {
    type Target = i32;
    
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
```


閲戝簡 2021-08-22 12:31 鍙戣〃璇勮
]]>
Rust Error Return Check Policyhttp://www.shnenglu.com/jinq0123/archive/2021/08/09/217775.html閲戝簡閲戝簡Mon, 09 Aug 2021 09:08:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/08/09/217775.htmlhttp://www.shnenglu.com/jinq0123/comments/217775.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/08/09/217775.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217775.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217775.htmlError Return Check Policy
  • Never use unwrap on Result. If the type is Err, it will panic and crash the program. The only exception is if it has already been checked for error previously or in test code.
  • Never use unwrap on Option for the same reason if the type is None as Result is Err.


閲戝簡 2021-08-09 17:08 鍙戣〃璇勮
]]>
Rust visibilityhttp://www.shnenglu.com/jinq0123/archive/2021/08/09/217774.html閲戝簡閲戝簡Mon, 09 Aug 2021 05:45:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/08/09/217774.htmlhttp://www.shnenglu.com/jinq0123/comments/217774.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/08/09/217774.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217774.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217774.htmlVisibility and privacy - The Rust Reference (rust-lang.org)

pub(in path)pub(crate)pub(super), and pub(self)

In addition to public and private, Rust allows users to declare an item as visible only within a given scope. The rules for pub restrictions are as follows:

  • pub(in path) makes an item visible within the provided pathpath must be an ancestor module of the item whose visibility is being declared.
  • pub(crate) makes an item visible within the current crate.
  • pub(super) makes an item visible to the parent module. This is equivalent to pub(in super).
  • pub(self) makes an item visible to the current module. This is equivalent to pub(in self) or not using pub at all.


閲戝簡 2021-08-09 13:45 鍙戣〃璇勮
]]>
Why does Rust check borrow even in single threadhttp://www.shnenglu.com/jinq0123/archive/2021/08/07/217773.html閲戝簡閲戝簡Sat, 07 Aug 2021 08:05:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/08/07/217773.htmlhttp://www.shnenglu.com/jinq0123/comments/217773.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/08/07/217773.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217773.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217773.html
# Why does Rust check borrow even in single thread

(Jin Qing's Column, Aug. 7, 2021)

The Rust book says borrow checking is to prevent data race.
But the borrow checker forbids multiply mutable borrows even in the same thread.
Is there data race in single thread?
Why does the borrow checker forbid it in the same thread?

[The Problem With Single-threaded Shared Mutability](https://manishearth.github.io/blog/2015/05/17/the-problem-with-shared-mutability/)
answers this question.

It gaves 2 cases that shared mutability causes prolem.
One is Rust enum variable, which can has different inner type.
If the inner type changed, the references to the old data would be invalidated.
Another case is Iterator invalidation that the container's change can invalidate the Iterator.




閲戝簡 2021-08-07 16:05 鍙戣〃璇勮
]]>UE4 Blueprint Multiple Event BeginPlayhttp://www.shnenglu.com/jinq0123/archive/2021/07/31/217765.html閲戝簡閲戝簡Sat, 31 Jul 2021 07:19:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/07/31/217765.htmlhttp://www.shnenglu.com/jinq0123/comments/217765.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/07/31/217765.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217765.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217765.html
(閲戝簡鐨勪笓鏍?2021.7)

How to do multiple actions on Event BeginPlay in UE4 Blueprints?

Sams-Teach-Yourself-Unreal-Engine-4-Game-Development-in-24-Hours says:
```
Q. When I try to add a second event node, BeginPlay, the Editor shows me the first one already
placed in the Event Graph. Why does this happen?

A. Some events, such as Event Tick and the BeginPlay event, can have only one instance per
Blueprint.
```

https://forums.unrealengine.com/t/do-multiple-things-on-event-begin-play/411/10
```
The Sequence node allows for a single execution pulse to trigger a series of events in order. The node may have any number of outputs, all of which get called as soon as the Sequence node receives an input. They will always get called in order, but without any delay. To a typical user, the outputs will likely appear to have been triggered simultaneously.
```

Youtube video: [How to do Multiple Actions on Event Begin Play Unreal Engine 4 Blueprints](https://www.youtube.com/watch?v=nqG-ztbs230)

閲戝簡 2021-07-31 15:19 鍙戣〃璇勮
]]>
Visual Studio 2019 Compiler Hangshttp://www.shnenglu.com/jinq0123/archive/2021/07/31/217764.html閲戝簡閲戝簡Sat, 31 Jul 2021 07:16:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/07/31/217764.htmlhttp://www.shnenglu.com/jinq0123/comments/217764.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/07/31/217764.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217764.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217764.html
(閲戝簡鐨勪笓鏍?2021.7)

Discovered by my colleague Shen Yichai:
```
Share a interesting MS Build bug:
For file a.cpp, enable optimization for Win64 or XSX.
The MSBuild always compiling without error and ending (infinite compile).
The PS5 (clang) does not have this issue.
```

The simplified code:
```
struct Tag
{
    int v;
};

void Goo(Tag* r, Tag* a)
{
    r->v = a->v;
}

void Foo(Tag* pos0, Tag* pos)
{
    for (int j = 0; j < 4; j++)
    {
        if (j == 0) {
            for (int i = 0; i < 8; i++) {
                Goo(pos0++, pos);
                Goo(pos0++, pos);
            }
        }
        else {
            for (int i = 0; i < 8; i++) {
                Goo(pos0++, pos);
                Goo(pos0++, pos);
            }
        }
    }
}

int main()
{
    Foo(nullptr, nullptr);
}
```

The default release configuration can build correctly.
But if "Properties -> C/C++ -> Optimization -> Whole Program optimization" (/GL) of file or project is changed to "No",
the compilation will take a long time as 5 minutes.

```
Microsoft Visual Studio Community 2019
Version 16.9.5
VisualStudio.16.Release/16.9.5+31229.75
Microsoft .NET Framework
Version 4.8.04084
```

閲戝簡 2021-07-31 15:16 鍙戣〃璇勮
]]>
Fbx File Format Identifierhttp://www.shnenglu.com/jinq0123/archive/2021/05/30/217692.html閲戝簡閲戝簡Sun, 30 May 2021 01:55:00 GMThttp://www.shnenglu.com/jinq0123/archive/2021/05/30/217692.htmlhttp://www.shnenglu.com/jinq0123/comments/217692.htmlhttp://www.shnenglu.com/jinq0123/archive/2021/05/30/217692.html#Feedback0http://www.shnenglu.com/jinq0123/comments/commentRss/217692.htmlhttp://www.shnenglu.com/jinq0123/services/trackbacks/217692.html
(閲戝簡鐨勪笓鏍?2021.5)

Print the list of FBX file format identifiers.

FBX SDK FbxExporter can set the file format identifier which is an int parameter.
The commnet says: User does not need to specify it by default.
If not specified, plugin will detect the file format according to file suffix automatically.

FBX SDK 2020 automatically set it to the latest binary format, which can not be parsed by "Autodesk FBX Converter 2013".
We need to specify the format identifier for FBX Converter 2013.
But there is no specification of this parameter.
We use the following code to display all the valid format identifiers and their descriptions.

```
#include <fbxsdk.h>
#include <cassert>

int main()
{
    FbxManager* myManager = FbxManager::Create();
    assert(myManager);

    FbxIOSettings* ios = FbxIOSettings::Create(myManager, IOSROOT);
    myManager->SetIOSettings(ios);

    int lFormatCount = myManager->GetIOPluginRegistry()->GetWriterFormatCount();
    for (int lFormatIndex = 0; lFormatIndex < lFormatCount; lFormatIndex++)
    {
        FbxString lDesc = myManager->GetIOPluginRegistry()->GetWriterFormatDescription(lFormatIndex);
        printf("Format index %d: %s\n", lFormatIndex, (const char*)lDesc);
    }
    return 0;
}
```

Output:
```
Format index 0: FBX binary (*.fbx)
Format index 1: FBX ascii (*.fbx)
Format index 2: FBX encrypted (*.fbx)
Format index 3: FBX 6.0 binary (*.fbx)
Format index 4: FBX 6.0 ascii (*.fbx)
Format index 5: FBX 6.0 encrypted (*.fbx)
Format index 6: AutoCAD DXF (*.dxf)
Format index 7: Alias OBJ (*.obj)
Format index 8: Collada DAE (*.dae)
Format index 9: Biovision BVH (*.bvh)
Format index 10: Motion Analysis HTR (*.htr)
Format index 11: Motion Analysis TRC (*.trc)
Format index 12: Acclaim ASF (*.asf)
Format index 13: Acclaim AMC (*.amc)
Format index 14: Vicon C3D (*.c3d)
Format index 15: Adaptive Optics AOA (*.aoa)
Format index 16: Superfluo MCD (*.mcd)
```

FBX Converter 2013 will error "File is corrupted" for files exported as *.fbx file format: -1, 0, 2, 5.
Ascii format (1, 4) and FBX 6.0 binary (3) are OK.


閲戝簡 2021-05-30 09:55 鍙戣〃璇勮
]]>
絎?浠f父鎴忎富鏈?/title><link>http://www.shnenglu.com/jinq0123/archive/2021/05/09/217680.html</link><dc:creator>閲戝簡</dc:creator><author>閲戝簡</author><pubDate>Sun, 09 May 2021 12:44:00 GMT</pubDate><guid>http://www.shnenglu.com/jinq0123/archive/2021/05/09/217680.html</guid><wfw:comment>http://www.shnenglu.com/jinq0123/comments/217680.html</wfw:comment><comments>http://www.shnenglu.com/jinq0123/archive/2021/05/09/217680.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/jinq0123/comments/commentRss/217680.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/jinq0123/services/trackbacks/217680.html</trackback:ping><description><![CDATA[# 絎?浠f父鎴忎富鏈?br /><br />鍘熸枃錛歔Ninth generation of video game consoles](https://en.wikipedia.org/wiki/Ninth_generation_of_video_game_consoles)<br /><br />2020.11錛屽井杞?MS) Xbox Series X/S 鍜?Sony PlayStation 5 (PS5) 鍙戝竷錛屾爣蹇楃潃娓告垙涓繪満榪涘叆絎?浠c?br /><br />鍜屽墠浠g殑 Xbox One 鍜?PS4 鐩告瘮錛屾柊涓浠d富鏈烘湁鍙鐨勬ц兘鎻愬崌錛屾敮鎸佸疄鏃跺厜綰胯窡韙紝4K鍒嗚鯨鐜囷紝鐩爣甯х巼涓?0銆?br />鍐呴儴閮戒嬌鐢ㄤ簡鍥烘佺‖鐩?SSD)銆備綆閰嶇増娌℃湁鍏夐┍錛屼粎鏀寔緗戠粶鍜孶SB銆?br /><br />瀹氫綅涓婅鑳滆繃浠誨ぉ鍫係witch鍜屼簯娓告垙鏈嶅姟濡?Stadia 鍜?Amazon Luna.<br /><br />## 鑳屾櫙<br /><br />絎?浠f椂闂磋緝闀褲傚洜涓烘懇灝斿畾寰嬶紝榪囧幓鍑犱唬涓鑸瘡浠d負5騫存椂闂達紝浣嗘槸 MS 鍜?Sony 鍑轟簡涓棿浠d駭鍝?Xbox One X 鍜?PS4 Pro.<br /><br />2020.3 寮濮嬬殑 COVID-19 鐤儏褰卞搷涔熶嬌鏂頒竴浠d富鏈虹殑鍙戝竷寤跺悗浜嗐?br /><br />## 涓昏涓繪満<br /><br />### PS5<br /><br />鏈?涓満鍨嬶紝鍩烘湰鍨嬪拰鏁板瓧鍨嬶紝鏁板瓧鍨嬫病鏈夊厜椹辮緝渚垮疁錛屽叾浠栦竴鏍楓?br />PS5鍜孭S4鐨勬父鎴忓吋瀹癸紝鍙湁灝戦噺娓告垙涓嶆敮鎸侊紝浣嗘槸鍙互閫氳繃 PS Now 浜戞父鎴忔湇鍔$帺 PS4 娓告垙銆?br /><br />### Xbox Series X/S<br /><br />MS 寤剁戶浜嗗弻涓繪満妯″紡錛氶珮绔殑X緋誨垪鍜屼綆绔殑S緋誨垪銆係緋誨垪娌℃湁鍏夐┍銆?br />涓よ呴兘鏀寔澶栭儴瀛樺偍鍜孹box Live鍦ㄧ嚎鍒嗗彂銆傚悜鍚庡吋瀹逛互鍓嶇殑娓告垙錛屼絾涓嶅寘鎷琄inect娓告垙銆?br />MS榧撳姳寮鍙戝晢浣跨敤 Smart Delivery錛屾妸Xbox One娓告垙鍗囩駭鍒癤box Series X/S銆?br /><br />## 鍏朵粬涓繪満<br /><br />* 浠誨ぉ鍫?Switch<br />* 浜戞父鎴忓鉤鍙幫細Stadia, Amazon Luna, GeForce Now<img src ="http://www.shnenglu.com/jinq0123/aggbug/217680.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/jinq0123/" target="_blank">閲戝簡</a> 2021-05-09 20:44 <a href="http://www.shnenglu.com/jinq0123/archive/2021/05/09/217680.html#Feedback" target="_blank" style="text-decoration:none;">鍙戣〃璇勮</a></div>]]></description></item><item><title>Canvas Scaler 鐨?縐嶆ā寮?/title><link>http://www.shnenglu.com/jinq0123/archive/2021/03/02/217621.html</link><dc:creator>閲戝簡</dc:creator><author>閲戝簡</author><pubDate>Tue, 02 Mar 2021 04:00:00 GMT</pubDate><guid>http://www.shnenglu.com/jinq0123/archive/2021/03/02/217621.html</guid><wfw:comment>http://www.shnenglu.com/jinq0123/comments/217621.html</wfw:comment><comments>http://www.shnenglu.com/jinq0123/archive/2021/03/02/217621.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/jinq0123/comments/commentRss/217621.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/jinq0123/services/trackbacks/217621.html</trackback:ping><description><![CDATA[Canvas Scaler 鐨?縐嶆ā寮?br /><br />(閲戝簡鐨勪笓鏍?2021.3)<br /><br />鍙傝冿細<br />https://wenku.baidu.com/view/83991090336c1eb91b375db8.html<br /><br />Unity Canvas 鏈変釜 Canvas Scaler 緇勪歡錛岀敤鏉ュ喅瀹?Canvas 鐨?Scale(緙╂斁)鍊箋?br />Scale 鏈?X,Y,Z 3涓垎閲忥紝閮芥槸鍚屼竴鍊鹼紝鍗沖湪鍚勪釜鏂瑰悜涓婇兘鏄悓鍊嶇巼緙╂斁鐨勩?br />瀹冩湁3縐峉cale Mode銆?br /><br />鍥犱負璁捐鏃剁殑瀹介珮姣斾笌鐩爣鏈哄瀷瀹介珮姣斾笉鍚岋紝Canvas 闇瑕佸厛澧炲噺涓虹洰鏍囬珮瀹芥瘮錛岀劧鍚庡啀緙╂斁銆?br />Canvas 緙╂斁騫朵笉褰卞搷UI鍏冪礌鐨勭浉浜掍綅緗紝鍙槸鍚屾瘮緙╂斁銆?br />浣嗘槸楂樺姣旇皟鏁存椂浼氶犳垚UI鍏冪礌鐨勭浉瀵逛綅緗彉鍖栵紝濡傚搴﹀澶у悗錛孶I鍏冪礌浼氬湪瀹藉害鏂瑰悜涓婄浉浜掓暎寮銆?br /><br />鍙互鍦?Unity Editor Game 欏靛乏涓婅鍒嗚鯨鐜囦笅鎷夎彍鍗曚腑鍒涘緩鍑犱釜浣庡垎杈ㄧ巼灞忥紝鐢ㄦ潵嫻嬭瘯灞忓箷澶у皬鍒囨崲銆?br />嫻嬭瘯鐣岄潰鍙斁緗?涓寜閽紝涓涓敋瀹氬埌宸︿笂錛屼綅浜庡乏涓婏紝涓涓敋瀹氬埌鍙充笅錛屼綅浜庡彸涓嬶紝涓嶈strech銆?br /><br />3縐嶆ā寮忓涓嬨?br /><br />## Constance Pixel Size<br /><br />Constance Pixel Size 妯″紡涓嬶紝Scale 鎬繪槸涓?銆?br />鐩爣鏈轟笂鐨?Canvas 灝辨槸鍏跺垎杈ㄧ巼澶у皬銆?br />鍥犱負璁捐鏃跺垎杈ㄧ巼涓庡疄闄呭垎杈ㄧ巼涓嶅悓錛屾樉紺轟細涓嶅悓銆?br />浣嗘槸鍥劇墖鍜屾寜閽殑澶у皬淇濇寔鍥哄畾鍍忕礌澶у皬銆?br />濡傛灉灞忓箷鍙樺ぇ錛孶I鍏冪礌灝變細鐩鎬簰鏁e紑錛屽彉灝忓垯鑱氭嫝銆?br />濡傛灉闇瑕佺緝鏀撅紝鍙兘閫氳繃紼嬪簭璋冭妭銆?br /><br />## Scale With Screen Size<br /><br />闅忓睆騫曞ぇ灝忕緝鏀俱?br />璇ユā寮忎笅鏈変釜 Reference Resolution (鍙傝冨垎杈ㄧ巼)錛屽簲璇ヨ涓轟富嫻佹満鍨嬬殑鍒嗚鯨鐜囥?br />鍦ㄤ富嫻佹満鍨嬩笅錛孲cale 涓?錛屼笌璁捐鏄劇ず鐩稿悓銆?br /><br />https://blog.csdn.net/u011948420/article/details/112641308<br />2020涓浗縐誨姩娓告垙璐ㄩ噺鐧界毊涔?WeTest 鎶ュ憡 Android 鎵嬫満 TOP300 鍒嗚鯨鐜囦負 2340*1080 鍗?31%銆?br /><br />濡傛灉鐩爣鏈哄瀷涓?4680*2160錛屽垯 Scale 姝eソ涓?錛涘鏋滅洰鏍囨満鍨嬩負 1179*540錛屽垯Scale涓?.5.<br /><br />涓鑸儏鍐典笅瀹介珮姣斾笉鍚岋紝姝ゆ椂Scale綆楁硶鏈?縐嶏紝鍗?縐峉ceen Match Mode錛?br /><br />### Match Width Or Height<br /><br />鍖歸厤瀹藉害鎴栭珮搴︺?br /><br />姝ゆ椂鏈変釜 Match 鍒掑姩鏉★紝鍙互鍦?Width(0)..Height(1)涔嬮棿銆?br />濡傛灉涓?錛屽尮閰嶅搴︼紝鍒?Scale 涓虹洰鏍囧睆瀹戒笌鍙傝冨睆瀹戒箣姣旓紱<br />濡傛灉涓?錛屽尮閰嶉珮搴︼紝鍒?Scale 涓虹洰鏍囧睆楂樹笌鍙傝冨睆楂樹箣姣旓紱<br />濡傛灉涓?.5, 鍔犳潈鍖歸厤錛屽垯鏄繖2涓瘮鍊肩殑騫沖潎鍊箋?br /><br />鍖歸厤瀹藉害鏃訛紝楂樺害鏂瑰悜涓婁細鏈塙I鎵╂暎鎴栬仛鎷€?br />鍖歸厤楂樺害鏃訛紝瀹藉害鏂瑰悜涓婁細鏈塙I鎵╂暎鎴栬仛鎷€?br />鍔犳潈鍖歸厤鏃訛紝UI鍦ㄩ珮涓婃墿鏁e涓婅仛鎷紝鎴栬呭湪楂樹笂鑱氭嫝瀹戒笂鎵╂暎銆?br /><br />### Expand<br /><br />澧炲姞銆傚厛澧炲姞瀹芥垨楂樺埌鐩爣瀹介珮姣斻傝澧炲姞鏂瑰悜UI鍏冪礌浼氭暎寮銆?br /><br />### Shrink<br /><br />鍑忓皯銆傚厛鍑忓皯瀹芥垨楂樺埌鐩爣瀹介珮姣斻傝鍑忓皯鏂瑰悜UI鍏冪礌浼氳仛鎷€?br /><br />## Constant Physical Size<br /><br />鍥哄畾鐗╃悊澶у皬銆?br />闅忕潃4k灞忕殑鍑虹幇錛屽睆騫旸PI浼氬緢澶э紝璁稿璁懼鐨勫儚绱犵偣闈炲父灝忋?br />鍚屾牱鐨勬寜閽紝鍦ㄩ珮DPI涓嬶紝濡傛灉鍥哄畾鍍忕礌澶у皬錛屽垯浼氶潪甯稿皬錛岃繖鏃墮傚悎鐢ㄨ妯″紡銆?br /><br /><img src ="http://www.shnenglu.com/jinq0123/aggbug/217621.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/jinq0123/" target="_blank">閲戝簡</a> 2021-03-02 12:00 <a href="http://www.shnenglu.com/jinq0123/archive/2021/03/02/217621.html#Feedback" target="_blank" style="text-decoration:none;">鍙戣〃璇勮</a></div>]]></description></item><item><title>C#寮傛鏂規(guī)硶榪斿洖void鍜孴ask鐨勫尯鍒?/title><link>http://www.shnenglu.com/jinq0123/archive/2021/02/25/217618.html</link><dc:creator>閲戝簡</dc:creator><author>閲戝簡</author><pubDate>Thu, 25 Feb 2021 02:38:00 GMT</pubDate><guid>http://www.shnenglu.com/jinq0123/archive/2021/02/25/217618.html</guid><wfw:comment>http://www.shnenglu.com/jinq0123/comments/217618.html</wfw:comment><comments>http://www.shnenglu.com/jinq0123/archive/2021/02/25/217618.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/jinq0123/comments/commentRss/217618.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/jinq0123/services/trackbacks/217618.html</trackback:ping><description><![CDATA[# C#寮傛鏂規(guī)硶榪斿洖void鍜孴ask鐨勫尯鍒?br /><br />(閲戝簡鐨勪笓鏍?2021.2)<br /><br />濡傛灉寮傛(async鍏抽敭瀛?鏂規(guī)硶鏈夎繑鍥炲鹼紝榪斿洖綾誨瀷涓篢鏃訛紝榪斿洖綾誨瀷蹇呯劧鏄?`Task<T>`銆?br />浣嗘槸濡傛灉娌℃湁榪斿洖鍊鹼紝寮傛鏂規(guī)硶鐨勮繑鍥炵被鍨嬫湁2縐嶏紝涓涓槸榪斿洖 Task, 涓涓槸榪斿洖 void錛?br />```<br />    public async Task CountDownAsync(int count)<br />    {<br />        for (int i = count; i >= 0; i--)<br />        {<br />            await Task.Delay(1000); <br />        }<br />    }<br /><br />    public async void CountDown(int count)<br />    {<br />        for (int i = count; i >= 0; i--)<br />        {<br />            await Task.Delay(1000);<br />        }<br />    }<br />```<br /><br />璋冪敤鏃訛紝濡傛灉榪斿洖 Task, 浣嗚繑鍥炲艱蹇界暐鏃訛紝VS 浼氱敤緇胯壊娉㈡氮綰胯鍛婏細<br />```<br />    CountDownAsync(3);<br />    ~~~~~~~~~~~~~~~~~<br />```<br /><br />淇℃伅涓猴細<br />```<br />(awaitable) Task AsyncExample.CountDownAsync(int count)<br /><br />Usage:<br /> await CountDownAsync(...);<br /><br />Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.<br />```<br /><br />涓枃涓猴細<br />```<br />CS4014:鐢變簬姝よ皟鐢ㄤ笉浼氱瓑寰咃紝鍥犳鍦ㄦ璋冪敤瀹屾垚涔嬪墠灝嗕細緇х畫鎵ц褰撳墠鏂規(guī)硶銆傝鑰冭檻灝?await"榪愮畻絎﹀簲鐢ㄤ簬璋冪敤緇撴灉銆?br />```<br /><br />娣誨姞 await 鍚庡氨姝e父浜嗭細<br />```<br />    await CountDownAsync(3);<br />```<br /><br />濡傛灉璋冪敤鑰呬笉鏄竴涓紓姝ユ柟娉曪紝鍥犱負鍙湁鍦ㄥ紓姝ユ柟娉曚腑鎵嶅彲浠ヤ嬌鐢?await,<br />鎴栬呭茍涓嶆兂鍦ㄦ絳夊緟錛屽鎯沖悓鏃舵墽琛屽涓?CountDownAsync(),<br />灝變笉鑳藉簲鐢?await 鏉ユ秷闄よ鍛娿?br /><br />姝ゆ椂鍙互鏀圭敤 void 榪斿洖鍊肩殑鐗堟湰錛?br />```<br />void Test()<br />{<br />    ...<br />    CountDown(3);<br />    CountDown(3);<br />    ...<br />}<br /><br />async void CountDown(int count)<br />{<br />    for (int i = count; i >= 0; i--)<br />    {<br />        await Task.Delay(1000);<br />    }<br />}<br />```<br /><br />> Never call `async Task` methods without also awaiting on the returned Task. If you don’t want to wait for the async behaviour to complete, you should call an `async void` method instead.<br /><br />鎽樿嚜錛歨ttp://www.stevevermeulen.com/index.php/2017/09/using-async-await-in-unity3d-2017/<br /><br />CountDown() 鍙互鐩存帴璋冪敤 CountDownAsync() 瀹炵幇錛?br />```<br />async void CountDown(int count)<br />{<br />    await CountDownAsync(count);<br />}<br />```<br /><br />浣跨敤涓嬪垝綰垮彉閲忓拷鐣ュ紓姝ユ柟娉曠殑榪斿洖鍊間篃鍙互娑堥櫎璀﹀憡錛?br />```<br />void Test()<br />{<br />    ...<br />    _ = CountDownAsync(3);<br />    _ = CountDownAsync(3);<br />    ...<br />}<br />```<br /><br />浣嗘槸榪欐牱鍚屾椂涔熶細蹇界暐 CountDownAsync() 涓殑寮傚父銆傚浠ヤ笅寮傚父浼氳蹇界暐銆?br /><br />```<br />void Test()<br />{<br />    ...<br />    _ = CountDownAsync(3);<br />    ...<br />}<br /><br />async Task CountDownAsync(int count)<br />{<br />    for (int i = count; i >= 0; i--)<br />    {<br />        await Task.Delay(1000); <br />    }<br />    throw new Exception();<br />}<br />```<br /><br />濡傛灉鏄皟鐢ㄨ繑鍥?void 鐨勫紓姝ユ柟娉曪紝Unity 浼氭姤閿欙細<br />```<br />Exception: Exception of type 'System.Exception' was thrown.<br />```<br /><br />## 瀵?Async 鍚庣紑鐨勮鏄?br /><br />```<br />You could say that the Async suffix convention is to communicate to the API user that the method is awaitable. For a method to be awaitable, it must return Task for a void, or Task<T> for a value-returning method, which means only the latter can be suffixed with Async.<br />```<br /><br />鎽樿嚜錛歨ttps://stackoverflow.com/questions/15951774<br /><br />grpc 鐢熸垚鐨勪唬鐮佷腑錛屽紓姝ヨ姹傝繑鍥炰簡涓涓?AsyncCall 瀵硅薄錛孉syncCall 瀹炵幇浜?GetAwaiter() 鎺ュ彛錛?br />```<br />      public virtual grpc::AsyncUnaryCall<global::Routeguide.Feature> GetFeatureAsync(global::Routeguide.Point request, ...)<br />```<br /><br />鍙互榪欐牱璋冪敤騫剁瓑寰咃細<br />```<br />    var resp = await client.GetFeatureAsync(req);<br />```<br /><br />铏界劧榪斿洖綾誨瀷涓嶆槸`Task<>`, 浣嗘槸鍙瓑寰咃紝鎵浠ユ坊鍔犱簡 Async 鍚庣紑銆?br /><img src ="http://www.shnenglu.com/jinq0123/aggbug/217618.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/jinq0123/" target="_blank">閲戝簡</a> 2021-02-25 10:38 <a href="http://www.shnenglu.com/jinq0123/archive/2021/02/25/217618.html#Feedback" target="_blank" style="text-decoration:none;">鍙戣〃璇勮</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>感谢您访问我们的网站,您可能还对以下资源感兴趣:</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.gyxcs.cn" target="_blank">91精品国产高清久久久久久91 </a>| <a href="http://www.ewokfijaa.cn" target="_blank">久久精品国产99国产电影网</a>| <a href="http://www.80649.cn" target="_blank">久久久久久噜噜精品免费直播</a>| <a href="http://www.daocheyingxiang.cn" target="_blank">久久久久亚洲AV无码网站</a>| <a href="http://www.cnsei.cn" target="_blank">亚洲人成伊人成综合网久久久</a>| <a href="http://www.uucity.com.cn" target="_blank">久久久久久亚洲精品不卡</a>| <a href="http://www.shyk888.cn" target="_blank">国产女人aaa级久久久级</a>| <a href="http://www.xcfsfl.cn" target="_blank">成人精品一区二区久久</a>| <a href="http://www.ccguofeng.cn" target="_blank">国产精品青草久久久久福利99</a>| <a href="http://www.ppxp.com.cn" target="_blank">九九久久99综合一区二区</a>| <a href="http://www.mengyun868.cn" target="_blank">99精品久久精品</a>| <a href="http://www.impark.cn" target="_blank">久久99精品综合国产首页</a>| <a href="http://www.hygame8888.cn" target="_blank">久久国产精品一区二区</a>| <a href="http://www.orange-cat.cn" target="_blank">91精品日韩人妻无码久久不卡 </a>| <a href="http://www.fengbiaochem.com.cn" target="_blank">久久久久国产一区二区三区</a>| <a href="http://www.chaikuo.cn" target="_blank">88久久精品无码一区二区毛片</a>| <a href="http://www.lantianhotel.cn" target="_blank">久久久九九有精品国产</a>| <a href="http://www.q126.cn" target="_blank">久久久国产精华液</a>| <a href="http://www.fttyz.com.cn" target="_blank">亚洲人成无码久久电影网站</a>| <a href="http://www.xmjbjd.cn" target="_blank">久久这里的只有是精品23</a>| <a href="http://www.hgrnoko.cn" target="_blank">日韩乱码人妻无码中文字幕久久</a>| <a href="http://www.xspree.cn" target="_blank">性欧美大战久久久久久久久</a>| <a href="http://www.cctt88.cn" target="_blank">久久久国产精品亚洲一区</a>| <a href="http://www.h8uq0.cn" target="_blank">久久福利青草精品资源站</a>| <a href="http://www.whchangxin.com.cn" target="_blank">国产精品gz久久久</a>| <a href="http://www.xibu520.cn" target="_blank">久久婷婷人人澡人人</a>| <a href="http://www.oxygeniclife.com.cn" target="_blank">国内精品综合久久久40p</a>| <a href="http://www.yingkounews.com.cn" target="_blank">亚洲va久久久噜噜噜久久男同</a>| <a href="http://www.zpktv.cn" target="_blank">97精品伊人久久大香线蕉app</a>| <a href="http://www.chezhuangbao.cn" target="_blank">久久久久四虎国产精品</a>| <a href="http://www.fzbn.net.cn" target="_blank">亚洲AV伊人久久青青草原</a>| <a href="http://www.cnbjwm.com.cn" target="_blank">午夜人妻久久久久久久久</a>| <a href="http://www.suntiepu.cn" target="_blank">久久亚洲高清观看</a>| <a href="http://www.sxzt888.cn" target="_blank">亚洲国产成人久久综合区</a>| <a href="http://www.0470gq.cn" target="_blank">日韩精品久久久肉伦网站</a>| <a href="http://www.weijiawu.cn" target="_blank">51久久夜色精品国产</a>| <a href="http://www.zxk2008bj.cn" target="_blank">久久精品国产亚洲av麻豆图片 </a>| <a href="http://www.vtsg.cn" target="_blank">久久精品一区二区三区AV</a>| <a href="http://www.mrzqjn.cn" target="_blank">97久久超碰国产精品2021</a>| <a href="http://www.cjbyt.cn" target="_blank">一级A毛片免费观看久久精品</a>| <a href="http://www.hbsannong.com.cn" target="_blank">日日噜噜夜夜狠狠久久丁香五月</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>